################################################################ ### HMN LOADER INSPECT NO-NL ################################################################ INSTANCE=mgts-main MODE=READ_ONLY REMOTE_CHANGES=NO ################################################################ ### REMOTE TOOL CHECK ################################################################ awk /usr/bin/awk sed /bin/sed grep /bin/grep uci /sbin/uci wg /usr/bin/wg ifup /sbin/ifup ifdown /sbin/ifdown sha256sum /usr/bin/sha256sum rc=0 ################################################################ ### SCRIPT INVENTORY ################################################################ --- /root/hmn/hmn-load-vpn-test.sh --- -rwxr-xr-x 1 root root 4949 Jun 3 16:20 /root/hmn/hmn-load-vpn-test.sh sha256 7407dea5a482e3b872a9d580ab54de238cb33b5331107eca59105fe3eb52c62a /root/hmn/hmn-load-vpn-test.sh --- /root/hmn/hmn-load-vpn-user.sh --- -rwxr-xr-x 1 root root 7032 Jul 6 14:24 /root/hmn/hmn-load-vpn-user.sh sha256 aaa86624aa5d4ea969053dc42ea386602bd4a0deddef8c13352205161e57f82f /root/hmn/hmn-load-vpn-user.sh --- /root/hmn/hmn-refill-slot.sh --- -rwxr-xr-x 1 root root 6084 Jun 3 16:20 /root/hmn/hmn-refill-slot.sh sha256 992b8075c9a5acbcd5c0f98feb03650acc7a211f9cc19afb9fd82ffc529bb053 /root/hmn/hmn-refill-slot.sh --- /root/hmn/hmn-apply-selected.sh --- -rwxr-xr-x 1 root root 6765 Jun 1 14:32 /root/hmn/hmn-apply-selected.sh sha256 ea283561a4acf7a781dd0abfeaab39467fec3a4abb2d3769cb98f9ca75f4c93e /root/hmn/hmn-apply-selected.sh --- /root/hmn/hmn-plan-selected.sh --- -rwxr-xr-x 1 root root 3181 Jun 1 15:12 /root/hmn/hmn-plan-selected.sh sha256 f7cf94b41f53f52a74b1c84f20e5119d91edff92d4587228e7ea41bb1a932b96 /root/hmn/hmn-plan-selected.sh --- /root/hmn/hmn-vpn-user-override.sh --- -rwxr-xr-x 1 root root 7379 Jul 6 10:00 /root/hmn/hmn-vpn-user-override.sh sha256 6e03209458dff3410976e193dfd83d06c3a888d7c1eb624b57def6f1913a1dd8 /root/hmn/hmn-vpn-user-override.sh --- /usr/bin/vpn-egress-manager.sh --- -rwxr-xr-x 1 root root 9700 Jul 6 10:00 /usr/bin/vpn-egress-manager.sh sha256 499688044940ff193e078c4198f17e361d59f58a2d84ce5821d77d607288d7d7 /usr/bin/vpn-egress-manager.sh rc=0 ################################################################ ### SANITIZED FULL LOADER CODE ################################################################ ### FILE /root/hmn/hmn-load-vpn-test.sh 1 #!/bin/ash 2 3 set -eu 4 5 CONF="${1:-}" 6 ACTION="${2:-}" 7 8 if [ -z "$CONF" ]; then 9 CONF="$(ls -t /root/hmn/configs/awg1/latest/*.conf 2>/dev/null | head -n 1 || true)" 10 fi 11 12 if [ -z "$CONF" ] || [ ! -f "$CONF" ]; then 13 echo "ERROR: не найден .conf" 14 echo 15 echo "Использование:" 16 echo " /root/hmn/hmn-load-vpn-test.sh /path/to/config.conf" 17 echo " /root/hmn/hmn-load-vpn-test.sh /path/to/config.conf up" 18 echo 19 echo "Или без аргумента — возьмёт первый файл из:" 20 echo " /root/hmn/configs/awg1/latest/" 21 exit 1 22 fi 23 24 getv() { 25 KEY="$1" 26 awk -v key="$KEY" ' 27 $0 ~ "^[[:space:]]*" key "[[:space:]]*=" { 28 sub(/^[^=]*=[[:space:]]*/, "", $0) 29 sub(/[[:space:]]*$/, "", $0) 30 print $0 31 exit 32 } 33 ' "$CONF" 34 } 35 36 PRIVATE_KEY=[REDACTED_PRIVATE_KEY] PrivateKey)" 37 ADDRESS="$(getv Address)" 38 DNS="$(getv DNS)" 39 JC="$(getv Jc)" 40 JMIN="$(getv Jmin)" 41 JMAX="$(getv Jmax)" 42 S1="$(getv S1)" 43 S2="$(getv S2)" 44 H1="$(getv H1)" 45 H2="$(getv H2)" 46 H3="$(getv H3)" 47 H4="$(getv H4)" 48 49 PUBLIC_KEY=[REDACTED_PUBLIC_KEY] PublicKey)" 50 ALLOWED_IPS="$(getv AllowedIPs)" 51 ENDPOINT="$(getv Endpoint)" 52 KEEPALIVE="$(getv PersistentKeepalive)" 53 54 ENDPOINT_HOST="${ENDPOINT%:*}" 55 ENDPOINT_PORT="${ENDPOINT##*:}" 56 57 if [ -z "$PRIVATE_KEY" ] || [ -z "$ADDRESS" ] || [ -z "$PUBLIC_KEY" ] || [ -z "$ENDPOINT_HOST" ] || [ -z "$ENDPOINT_PORT" ]; then 58 echo "ERROR: конфиг не удалось распарсить." 59 echo 60 echo "CONF=$CONF" 61 echo "Address=$ADDRESS" 62 echo "PrivateKey=[REDACTED_PRIVATE_KEY] 63 echo "PublicKey=[REDACTED_PUBLIC_KEY] 64 echo "Endpoint=$ENDPOINT" 65 exit 1 66 fi 67 68 TS="$(date +%Y%m%d-%H%M%S)" 69 BACKUP="/root/hmn/backups/network-before-vpn_test-$TS" 70 71 mkdir -p /root/hmn/backups 72 chmod 700 /root/hmn /root/hmn/backups 73 74 cp /etc/config/network "$BACKUP" 75 chmod 600 "$BACKUP" 76 77 echo "Backup:" 78 echo " $BACKUP" 79 echo 80 echo "Loading config into vpn_test:" 81 echo " $CONF" 82 echo 83 echo "Parsed:" 84 echo " Address: $ADDRESS" 85 echo " Endpoint: $ENDPOINT" 86 echo " DNS: ${DNS:-none}" 87 echo " AWG: Jc=$JC Jmin=$JMIN Jmax=$JMAX S1=$S1 S2=$S2" 88 echo 89 90 ifdown vpn_test 2>/dev/null || true 91 92 uci -q delete network.vpn_test 93 94 while :; do 95 SEC="$(uci -q show network | sed -n 's/^\(network\.@amneziawg_vpn_test\[[0-9][0-9]*\]\)=amneziawg_vpn_test$/\1/p' | head -n 1)" 96 [ -n "$SEC" ] || break 97 uci -q delete "$SEC" 98 done 99 100 uci set network.vpn_test='interface' 101 uci set network.vpn_test.proto='amneziawg' 102 uci set network.vpn_test.private_key=[REDACTED_PRIVATE_KEY] 103 uci set network.vpn_test.awg_jc="$JC" 104 uci set network.vpn_test.awg_jmin="$JMIN" 105 uci set network.vpn_test.awg_jmax="$JMAX" 106 uci set network.vpn_test.awg_s1="$S1" 107 uci set network.vpn_test.awg_s2="$S2" 108 uci set network.vpn_test.awg_h1="$H1" 109 uci set network.vpn_test.awg_h2="$H2" 110 uci set network.vpn_test.awg_h3="$H3" 111 uci set network.vpn_test.awg_h4="$H4" 112 113 uci set network.vpn_test.auto='0' 114 uci set network.vpn_test.disabled='0' 115 uci set network.vpn_test.delegate='0' 116 uci set network.vpn_test.peerdns='0' 117 uci set network.vpn_test.defaultroute='0' 118 119 uci add_list network.vpn_test.addresses="$ADDRESS" 120 121 if [ -n "${DNS:-}" ]; then 122 OLDIFS="$IFS" 123 IFS=',' 124 for D in $DNS; do 125 D="$(echo "$D" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" 126 [ -n "$D" ] && uci add_list network.vpn_test.dns="$D" 127 done 128 IFS="$OLDIFS" 129 fi 130 131 PEER="$(uci add network amneziawg_vpn_test)" 132 uci set network."$PEER".description="$(basename "$CONF")" 133 uci set network."$PEER".public_key=[REDACTED_PUBLIC_KEY] 134 135 OLDIFS="$IFS" 136 IFS=',' 137 for A in ${ALLOWED_IPS:-0.0.0.0/0}; do 138 A="$(echo "$A" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" 139 [ -n "$A" ] && uci add_list network."$PEER".allowed_ips="$A" 140 done 141 IFS="$OLDIFS" 142 143 uci set network."$PEER".route_allowed_ips='0' 144 uci set network."$PEER".persistent_keepalive="${KEEPALIVE:-25}" 145 uci set network."$PEER".endpoint_host="$ENDPOINT_HOST" 146 uci set network."$PEER".endpoint_port="$ENDPOINT_PORT" 147 148 uci set network.vpn_test.hmn_role='test_slot' 149 uci set network.vpn_test.hmn_source_config="$CONF" 150 uci set network.vpn_test.hmn_loaded_at="$(date -Iseconds)" 151 uci set network.vpn_test.hmn_endpoint="$ENDPOINT" 152 153 uci commit network 154 155 /etc/init.d/network reload 156 157 echo 158 echo "vpn_test loaded." 159 echo 160 uci show network.vpn_test 161 echo 162 uci show network | grep -E '=amneziawg_vpn_test|@amneziawg_vpn_test.*public_key|@amneziawg_vpn_test.*allowed_ips|@amneziawg_vpn_test.*route_allowed_ips|@amneziawg_vpn_test.*endpoint_host|@amneziawg_vpn_test.*endpoint_port|@amneziawg_vpn_test.*persistent_keepalive' 163 echo 164 165 echo "Current table 200:" 166 ip route show table 200 167 echo 168 169 if [ "$ACTION" = "up" ]; then 170 echo "Bringing vpn_test up..." 171 ifup vpn_test 172 sleep 5 173 174 echo 175 echo "Link:" 176 ip link show vpn_test 2>/dev/null || true 177 178 echo 179 echo "AWG/WG show:" 180 awg show vpn_test 2>/dev/null || wg show vpn_test 2>/dev/null || true 181 182 echo 183 echo "Table 200 after ifup:" 184 ip route show table 200 185 else 186 echo "Не поднимал интерфейс." 187 echo "Чтобы поднять:" 188 echo " ifup vpn_test" 189 fi ### FILE /root/hmn/hmn-load-vpn-user.sh 1 #!/bin/ash 2 3 set -eu 4 5 CONF="${1:-}" 6 ACTION="${2:-}" 7 8 if [ -z "$CONF" ]; then 9 CONF="$(ls -t /root/hmn/configs/awg1/latest/*.conf 2>/dev/null | head -n 1 || true)" 10 fi 11 12 if [ -z "$CONF" ] || [ ! -f "$CONF" ]; then 13 echo "ERROR: не найден .conf" 14 echo 15 echo "Использование:" 16 echo " /root/hmn/hmn-load-vpn-test.sh /path/to/config.conf" 17 echo " /root/hmn/hmn-load-vpn-test.sh /path/to/config.conf up" 18 echo 19 echo "Или без аргумента — возьмёт первый файл из:" 20 echo " /root/hmn/configs/awg1/latest/" 21 exit 1 22 fi 23 24 getv() { 25 KEY="$1" 26 awk -v key="$KEY" ' 27 $0 ~ "^[[:space:]]*" key "[[:space:]]*=" { 28 sub(/^[^=]*=[[:space:]]*/, "", $0) 29 sub(/[[:space:]]*$/, "", $0) 30 print $0 31 exit 32 } 33 ' "$CONF" 34 } 35 36 PRIVATE_KEY=[REDACTED_PRIVATE_KEY] PrivateKey)" 37 ADDRESS="$(getv Address)" 38 DNS="$(getv DNS)" 39 JC="$(getv Jc)" 40 JMIN="$(getv Jmin)" 41 JMAX="$(getv Jmax)" 42 S1="$(getv S1)" 43 S2="$(getv S2)" 44 H1="$(getv H1)" 45 H2="$(getv H2)" 46 H3="$(getv H3)" 47 H4="$(getv H4)" 48 49 PUBLIC_KEY=[REDACTED_PUBLIC_KEY] PublicKey)" 50 ALLOWED_IPS="$(getv AllowedIPs)" 51 ENDPOINT="$(getv Endpoint)" 52 KEEPALIVE="$(getv PersistentKeepalive)" 53 54 ENDPOINT_HOST="${ENDPOINT%:*}" 55 ENDPOINT_PORT="${ENDPOINT##*:}" 56 57 if [ -z "$PRIVATE_KEY" ] || [ -z "$ADDRESS" ] || [ -z "$PUBLIC_KEY" ] || [ -z "$ENDPOINT_HOST" ] || [ -z "$ENDPOINT_PORT" ]; then 58 echo "ERROR: конфиг не удалось распарсить." 59 echo 60 echo "CONF=$CONF" 61 echo "Address=$ADDRESS" 62 echo "PrivateKey=[REDACTED_PRIVATE_KEY] 63 echo "PublicKey=[REDACTED_PUBLIC_KEY] 64 echo "Endpoint=$ENDPOINT" 65 exit 1 66 fi 67 68 TS="$(date +%Y%m%d-%H%M%S)" 69 70 # HMN_DUPLICATE_ACTIVE_SLOT_GUARD_V1 71 # Do not load the same AWG endpoint/config into vpn_user if it is already present 72 # in managed egress slots. Running the same remote tunnel twice on different 73 # interfaces can break routing/handshake behavior. 74 check_duplicate_active_slots() { 75 TARGET_EP="${ENDPOINT_HOST}:${ENDPOINT_PORT}" 76 TARGET_CONF_BASE="$(basename "${CONF:-}" 2>/dev/null || echo "")" 77 78 for SLOT in vpn1 vpn2 vpn3 vpn4; do 79 [ "$SLOT" = "vpn_user" ] && continue 80 81 # Compare by interface metadata if present. 82 SLOT_SRC="$(uci -q get network.${SLOT}.hmn_source_config || true)" 83 if [ -n "$TARGET_CONF_BASE" ] && [ -n "$SLOT_SRC" ]; then 84 SLOT_SRC_BASE="$(basename "$SLOT_SRC" 2>/dev/null || echo "")" 85 if [ "$TARGET_CONF_BASE" = "$SLOT_SRC_BASE" ]; then 86 echo "ERROR: selected config already loaded in $SLOT: $TARGET_CONF_BASE" 87 exit 1 88 fi 89 fi 90 91 # Compare by peer endpoint in amneziawg slot section. 92 SEC="$(uci -q show network | sed -n "s/^\(network\.@amneziawg_${SLOT}\[[0-9][0-9]*\]\)=amneziawg_${SLOT}$/\1/p" | head -n 1)" 93 if [ -n "$SEC" ]; then 94 EH="$(uci -q get ${SEC}.endpoint_host || true)" 95 EP="$(uci -q get ${SEC}.endpoint_port || true)" 96 if [ -n "$EH" ] && [ -n "$EP" ] && [ "$TARGET_EP" = "$EH:$EP" ]; then 97 echo "ERROR: selected endpoint already loaded in $SLOT: $TARGET_EP" 98 exit 1 99 fi 100 fi 101 done 102 } 103 104 check_duplicate_active_slots 105 106 BACKUP="/root/hmn/backups/network-before-vpn_user-$TS" 107 108 mkdir -p /root/hmn/backups 109 chmod 700 /root/hmn /root/hmn/backups 110 111 cp /etc/config/network "$BACKUP" 112 chmod 600 "$BACKUP" 113 114 echo "Backup:" 115 echo " $BACKUP" 116 echo 117 echo "Loading config into vpn_user:" 118 echo " $CONF" 119 echo 120 echo "Parsed:" 121 echo " Address: $ADDRESS" 122 echo " Endpoint: $ENDPOINT" 123 echo " DNS: ${DNS:-none}" 124 echo " AWG: Jc=$JC Jmin=$JMIN Jmax=$JMAX S1=$S1 S2=$S2" 125 echo 126 127 ifdown vpn_user 2>/dev/null || true 128 129 uci -q delete network.vpn_user 2>/dev/null || true 130 131 while :; do 132 SEC="$(uci -q show network | sed -n 's/^\(network\.@amneziawg_vpn_user\[[0-9][0-9]*\]\)=amneziawg_vpn_user$/\1/p' | head -n 1)" 133 [ -n "$SEC" ] || break 134 uci -q delete "$SEC" 135 done 136 137 uci set network.vpn_user='interface' 138 uci set network.vpn_user.proto='amneziawg' 139 uci set network.vpn_user.private_key=[REDACTED_PRIVATE_KEY] 140 uci set network.vpn_user.awg_jc="$JC" 141 uci set network.vpn_user.awg_jmin="$JMIN" 142 uci set network.vpn_user.awg_jmax="$JMAX" 143 uci set network.vpn_user.awg_s1="$S1" 144 uci set network.vpn_user.awg_s2="$S2" 145 uci set network.vpn_user.awg_h1="$H1" 146 uci set network.vpn_user.awg_h2="$H2" 147 uci set network.vpn_user.awg_h3="$H3" 148 uci set network.vpn_user.awg_h4="$H4" 149 150 uci set network.vpn_user.auto='0' 151 uci set network.vpn_user.disabled='0' 152 uci set network.vpn_user.delegate='0' 153 uci set network.vpn_user.peerdns='0' 154 uci set network.vpn_user.defaultroute='0' 155 156 uci add_list network.vpn_user.addresses="$ADDRESS" 157 158 if [ -n "${DNS:-}" ]; then 159 OLDIFS="$IFS" 160 IFS=',' 161 for D in $DNS; do 162 D="$(echo "$D" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" 163 [ -n "$D" ] && uci add_list network.vpn_user.dns="$D" 164 done 165 IFS="$OLDIFS" 166 fi 167 168 PEER="$(uci add network amneziawg_vpn_user)" 169 uci set network."$PEER".description="$(basename "$CONF")" 170 uci set network."$PEER".public_key=[REDACTED_PUBLIC_KEY] 171 172 OLDIFS="$IFS" 173 IFS=',' 174 for A in ${ALLOWED_IPS:-0.0.0.0/0}; do 175 A="$(echo "$A" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" 176 [ -n "$A" ] && uci add_list network."$PEER".allowed_ips="$A" 177 done 178 IFS="$OLDIFS" 179 180 uci set network."$PEER".route_allowed_ips='0' 181 uci set network."$PEER".persistent_keepalive="${KEEPALIVE:-25}" 182 uci set network."$PEER".endpoint_host="$ENDPOINT_HOST" 183 uci set network."$PEER".endpoint_port="$ENDPOINT_PORT" 184 185 uci set network.vpn_user.hmn_role='user_select_slot' 186 uci set network.vpn_user.hmn_source_config="$CONF" 187 uci set network.vpn_user.hmn_loaded_at="$(date -Iseconds)" 188 uci set network.vpn_user.hmn_endpoint="$ENDPOINT" 189 190 uci commit network 191 192 /etc/init.d/network reload 193 194 echo 195 echo "vpn_user loaded." 196 echo 197 echo "vpn_user summary:" 198 echo " proto=$(uci -q get network.vpn_user.proto || true)" 199 echo " address=$(uci -q get network.vpn_user.addresses || true)" 200 echo " dns=$(uci -q get network.vpn_user.dns || true)" 201 echo " source_config=$(uci -q get network.vpn_user.hmn_source_config || true)" 202 echo " endpoint=$(uci -q get network.vpn_user.hmn_endpoint || true)" 203 PEER_SUM="$(uci -q show network | sed -n 's/^\(network\.@amneziawg_vpn_user\[[0-9][0-9]*\]\)=amneziawg_vpn_user$/\1/p' | head -n 1)" 204 if [ -n "$PEER_SUM" ]; then 205 echo " peer_section=$PEER_SUM" 206 echo " allowed_ips=$(uci -q get ${PEER_SUM}.allowed_ips || true)" 207 echo " route_allowed_ips=$(uci -q get ${PEER_SUM}.route_allowed_ips || true)" 208 echo " persistent_keepalive=$(uci -q get ${PEER_SUM}.persistent_keepalive || true)" 209 echo " peer_endpoint=$(uci -q get ${PEER_SUM}.endpoint_host || true):$(uci -q get ${PEER_SUM}.endpoint_port || true)" 210 fi 211 echo 212 213 echo "Current table 200:" 214 ip route show table 200 215 echo 216 217 if [ "$ACTION" = "up" ]; then 218 echo "Bringing vpn_user up..." 219 ifup vpn_user 220 sleep 5 221 222 echo 223 echo "Link:" 224 ip link show vpn_user 2>/dev/null || true 225 226 echo 227 echo "AWG/WG show: hidden in normal output; use explicit admin diagnostics if needed" 228 229 echo 230 echo "Table 200 after ifup:" 231 ip route show table 200 232 else 233 echo "Не поднимал интерфейс." 234 echo "Чтобы поднять:" 235 echo " ifup vpn_user" 236 fi ### FILE /root/hmn/hmn-refill-slot.sh 1 #!/bin/ash 2 3 set -u 4 5 BASE="/root/hmn" 6 TABLE="${TABLE:-$BASE/cache/ok-awg1-strict-foreign-latest.tsv}" 7 SELECTED="${SELECTED:-$BASE/cache/selected-awg1-latest.tsv}" 8 LOADER="${LOADER:-$BASE/hmn-load-vpn-slot.sh}" 9 PING_COUNT="${PING_COUNT:-5}" 10 DRY_RUN="${DRY_RUN:-0}" 11 12 SLOT="${1:-}" 13 ACTIVE_SLOT="${2:-}" 14 15 if [ -z "$SLOT" ] || [ -z "$ACTIVE_SLOT" ]; then 16 echo "Usage: $0 " >&2 17 echo "Example: DRY_RUN=1 $0 vpn1 vpn2" >&2 18 exit 2 19 fi 20 21 [ "$SLOT" = "vpn1" ] || [ "$SLOT" = "vpn2" ] || { 22 echo "ERROR: slot must be vpn1 or vpn2" >&2 23 exit 2 24 } 25 26 [ "$ACTIVE_SLOT" = "vpn1" ] || [ "$ACTIVE_SLOT" = "vpn2" ] || { 27 echo "ERROR: active slot must be vpn1 or vpn2" >&2 28 exit 2 29 } 30 31 [ -s "$TABLE" ] || { 32 echo "ERROR: OK table missing: $TABLE" >&2 33 exit 1 34 } 35 36 [ -x "$LOADER" ] || { 37 echo "ERROR: loader missing/not executable: $LOADER" >&2 38 exit 1 39 } 40 41 today_bad_file() { 42 date +"/root/hmn/state/bad-endpoints-%Y%m%d.txt" 43 } 44 45 get_slot_ep() { 46 S="$1" 47 H="$(uci -q get network.@amneziawg_${S}[0].endpoint_host || true)" 48 P="$(uci -q get network.@amneziawg_${S}[0].endpoint_port || true)" 49 [ -n "$H" ] && [ -n "$P" ] && echo "$H:$P" 50 } 51 52 mark_bad_ep() { 53 EP="$1" 54 REASON="${2:-failed}" 55 [ -n "$EP" ] || return 0 56 57 mkdir -p "$BASE/state" 58 BAD="$(today_bad_file)" 59 60 grep -q "^$EP" "$BAD" 2>/dev/null && return 0 61 printf "%s\t%s\t%s\n" "$EP" "$(date -Iseconds)" "$REASON" >> "$BAD" 62 } 63 64 is_bad_ep() { 65 EP="$1" 66 BAD="$(today_bad_file)" 67 grep -q "^$EP" "$BAD" 2>/dev/null 68 } 69 70 candidate_line() { 71 ACTIVE_EP="$(get_slot_ep "$ACTIVE_SLOT" || true)" 72 OLD_EP="$(get_slot_ep "$SLOT" || true)" 73 74 awk -F '\t' -v active_ep="$ACTIVE_EP" -v old_ep="$OLD_EP" -v bad_file="$(today_bad_file)" ' 75 BEGIN { 76 while ((getline line < bad_file) > 0) { 77 split(line, a, "\t") 78 bad[a[1]]=1 79 } 80 close(bad_file) 81 } 82 83 NR > 1 { 84 ep=$3 85 if (ep == active_ep) next 86 if (ep == old_ep) next 87 if (bad[ep]) next 88 print 89 exit 90 } 91 ' "$TABLE" 92 } 93 94 probe_slot_strict() { 95 DEV="$1" 96 97 for IP in 9.9.9.9 1.1.1.1; do 98 echo 99 echo "--- strict probe $DEV $IP ---" 100 ip route replace "$IP/32" dev "$DEV" 101 ip route get "$IP" 102 OUT="$(ping -4 -c "$PING_COUNT" -W 2 -I "$DEV" "$IP" 2>&1 || true)" 103 echo "$OUT" 104 ip route del "$IP/32" dev "$DEV" 2>/dev/null || true 105 106 echo "$OUT" | grep -q ' 0% packet loss' || return 1 107 done 108 109 return 0 110 } 111 112 echo "=== hmn-refill-slot ===" 113 echo "slot_to_refill=$SLOT" 114 echo "active_slot=$ACTIVE_SLOT" 115 echo "table=$TABLE" 116 echo "selected=$SELECTED" 117 echo "dry_run=$DRY_RUN" 118 119 ACTIVE_EP="$(get_slot_ep "$ACTIVE_SLOT" || true)" 120 OLD_EP="$(get_slot_ep "$SLOT" || true)" 121 122 echo "active_ep=$ACTIVE_EP" 123 echo "old_slot_ep=$OLD_EP" 124 125 if [ -n "$OLD_EP" ]; then 126 if [ "$DRY_RUN" = "1" ]; then 127 echo "would_mark_bad_ep=$OLD_EP" 128 else 129 mark_bad_ep "$OLD_EP" "slot_refill_old_or_failed_${SLOT}" 130 fi 131 fi 132 133 LINE="$(candidate_line || true)" 134 135 if [ -z "$LINE" ]; then 136 echo "ERROR: no candidate available after exclusions" 137 echo 138 echo "bad endpoints today:" 139 cat "$(today_bad_file)" 2>/dev/null || true 140 exit 1 141 fi 142 143 CAND_RANK="$(echo "$LINE" | awk -F '\t' '{print $1}')" 144 CAND_FILE="$(echo "$LINE" | awk -F '\t' '{print $2}')" 145 CAND_EP="$(echo "$LINE" | awk -F '\t' '{print $3}')" 146 CAND_AVG="$(echo "$LINE" | awk -F '\t' '{print $4}')" 147 CAND_CONF="$(echo "$LINE" | awk -F '\t' '{print $6}')" 148 149 echo 150 echo "candidate_rank=$CAND_RANK" 151 echo "candidate_file=$CAND_FILE" 152 echo "candidate_ep=$CAND_EP" 153 echo "candidate_avg=$CAND_AVG" 154 echo "candidate_conf=$CAND_CONF" 155 156 [ -s "$CAND_CONF" ] || { 157 echo "ERROR: candidate config missing: $CAND_CONF" 158 exit 1 159 } 160 161 if [ "$DRY_RUN" = "1" ]; then 162 echo 163 echo "DRY_RUN=1, not loading anything" 164 echo 165 echo "bad endpoints today:" 166 cat "$(today_bad_file)" 2>/dev/null || true 167 exit 0 168 fi 169 170 echo 171 echo "=== load candidate into $SLOT ===" 172 ifdown "$SLOT" 2>/dev/null || true 173 sleep 2 174 175 "$LOADER" "$SLOT" "$CAND_CONF" 176 177 echo 178 echo "=== reload netifd so changed slot is visible ===" 179 ubus call network reload 2>/dev/null || /etc/init.d/network reload 180 sleep 8 181 182 echo 183 echo "=== restore active slot after reload ===" 184 ifup "$ACTIVE_SLOT" 2>/dev/null || true 185 sleep 8 186 187 echo "$ACTIVE_SLOT" > "$BASE/state/active-slot" 188 ip route replace default dev "$ACTIVE_SLOT" table 200 189 /usr/bin/vpn-table200-local-routes.sh 2>/dev/null || true 190 ip route flush cache 191 192 echo 193 echo "=== bring candidate slot up for strict probe ===" 194 ifup "$SLOT" 195 sleep 22 196 197 echo 198 echo "--- $SLOT status ---" 199 /usr/bin/amneziawg show "$SLOT" 2>/dev/null | sed -n '1,35p' || true 200 201 if ! ip link show "$SLOT" >/dev/null 2>&1; then 202 echo "ERROR: $SLOT link absent after ifup" 203 mark_bad_ep "$CAND_EP" "link_absent_after_load_${SLOT}" 204 exit 1 205 fi 206 207 if ! probe_slot_strict "$SLOT"; then 208 echo "ERROR: candidate failed strict probe" 209 mark_bad_ep "$CAND_EP" "strict_probe_failed_${SLOT}" 210 ifdown "$SLOT" 2>/dev/null || true 211 exit 1 212 fi 213 214 echo 215 echo "=== update selected cache ===" 216 cp -a "$SELECTED" "$SELECTED.before-refill-$(date +%Y%m%d-%H%M%S)" 2>/dev/null || true 217 218 OTHER_SLOT="$ACTIVE_SLOT" 219 OTHER_EP="$(get_slot_ep "$OTHER_SLOT" || true)" 220 221 { 222 printf "slot\tfile\tendpoint\tavg_ms\tconfig_path\n" 223 printf "%s\t%s\t%s\t%s\t%s\n" "$SLOT" "$CAND_FILE" "$CAND_EP" "$CAND_AVG" "$CAND_CONF" 224 225 awk -F '\t' -v s="$OTHER_SLOT" -v ep="$OTHER_EP" 'NR>1 && $1==s { 226 printf "%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5 227 found=1 228 } 229 END { 230 if (!found && ep != "") { 231 # leave absent rather than inventing config path 232 } 233 }' "$SELECTED" 234 } > /tmp/selected-awg1-refill.tsv 235 236 cp /tmp/selected-awg1-refill.tsv "$SELECTED" 237 rm -f /tmp/selected-awg1-refill.tsv 238 239 cat "$SELECTED" 240 241 echo 242 echo "=== leave refilled slot down as standby ===" 243 ifdown "$SLOT" 2>/dev/null || true 244 sleep 4 245 246 echo 247 echo "=== final route should remain active slot ===" 248 echo "$ACTIVE_SLOT" > "$BASE/state/active-slot" 249 ip route replace default dev "$ACTIVE_SLOT" table 200 250 /usr/bin/vpn-table200-local-routes.sh 2>/dev/null || true 251 ip route flush cache 252 253 cat "$BASE/state/active-slot" 254 ip route show table 200 255 ip route get 9.9.9.9 from 10.200.0.2 256 257 echo 258 echo "=== refill done ===" ### FILE /root/hmn/hmn-apply-selected.sh 1 #!/bin/ash 2 3 set -eu 4 5 MODE="${1:-dry-run}" 6 SELECTED="${HMN_SELECTED_FILE:-/root/hmn/cache/selected-awg1-latest.tsv}" 7 QUARANTINE="${HMN_QUARANTINE_FILE:-/root/hmn/cache/quarantine-awg1-latest.tsv}" 8 PING_IP="${HMN_STANDBY_PING_IP:-9.9.9.9}" 9 MAINTENANCE_FILE="/tmp/hmn-vpn-maintenance" 10 11 case "$MODE" in 12 dry-run|standby-only) 13 ;; 14 *) 15 echo "ERROR: mode must be dry-run or standby-only" 16 echo "Usage:" 17 echo " /root/hmn/hmn-apply-selected.sh dry-run" 18 echo " /root/hmn/hmn-apply-selected.sh standby-only" 19 exit 1 20 ;; 21 esac 22 23 if [ ! -f "$SELECTED" ]; then 24 echo "ERROR: selected file not found:" 25 echo " $SELECTED" 26 exit 1 27 fi 28 29 ACTIVE="$(cat /root/hmn/state/active-slot 2>/dev/null || true)" 30 31 case "$ACTIVE" in 32 vpn1) STANDBY="vpn2" ;; 33 vpn2) STANDBY="vpn1" ;; 34 *) 35 echo "ERROR: unknown active slot: $ACTIVE" 36 exit 1 37 ;; 38 esac 39 40 ACTIVE_ENDPOINT="$(uci -q get network.$ACTIVE.hmn_endpoint || true)" 41 ACTIVE_CONFIG="$(uci -q get network.$ACTIVE.hmn_source_config || true)" 42 43 STANDBY_ENDPOINT="$(uci -q get network.$STANDBY.hmn_endpoint || true)" 44 STANDBY_CONFIG="$(uci -q get network.$STANDBY.hmn_source_config || true)" 45 46 BEST_FILE="$(awk -F '\t' 'NR==2 {print $2}' "$SELECTED")" 47 BEST_ENDPOINT="$(awk -F '\t' 'NR==2 {print $3}' "$SELECTED")" 48 BEST_AVG="$(awk -F '\t' 'NR==2 {print $4}' "$SELECTED")" 49 BEST_CONFIG="$(awk -F '\t' 'NR==2 {print $5}' "$SELECTED")" 50 51 SECOND_FILE="$(awk -F '\t' 'NR==3 {print $2}' "$SELECTED")" 52 SECOND_ENDPOINT="$(awk -F '\t' 'NR==3 {print $3}' "$SELECTED")" 53 SECOND_AVG="$(awk -F '\t' 'NR==3 {print $4}' "$SELECTED")" 54 SECOND_CONFIG="$(awk -F '\t' 'NR==3 {print $5}' "$SELECTED")" 55 56 if [ -z "$BEST_ENDPOINT" ] || [ -z "$BEST_CONFIG" ] || [ -z "$SECOND_ENDPOINT" ] || [ -z "$SECOND_CONFIG" ]; then 57 echo "ERROR: selected file does not contain two candidates:" 58 echo " $SELECTED" 59 cat "$SELECTED" 60 exit 1 61 fi 62 63 if [ "$ACTIVE_ENDPOINT" = "$BEST_ENDPOINT" ]; then 64 DESIRED_FILE="$SECOND_FILE" 65 DESIRED_ENDPOINT="$SECOND_ENDPOINT" 66 DESIRED_AVG="$SECOND_AVG" 67 DESIRED_CONFIG="$SECOND_CONFIG" 68 REASON="active_has_best_use_second_as_standby" 69 elif [ "$ACTIVE_ENDPOINT" = "$SECOND_ENDPOINT" ]; then 70 DESIRED_FILE="$BEST_FILE" 71 DESIRED_ENDPOINT="$BEST_ENDPOINT" 72 DESIRED_AVG="$BEST_AVG" 73 DESIRED_CONFIG="$BEST_CONFIG" 74 REASON="active_has_second_use_best_as_standby" 75 else 76 DESIRED_FILE="$BEST_FILE" 77 DESIRED_ENDPOINT="$BEST_ENDPOINT" 78 DESIRED_AVG="$BEST_AVG" 79 DESIRED_CONFIG="$BEST_CONFIG" 80 REASON="active_not_in_selected_use_best_as_standby" 81 fi 82 83 if [ ! -f "$DESIRED_CONFIG" ]; then 84 echo "ERROR: desired standby config not found:" 85 echo " $DESIRED_CONFIG" 86 exit 1 87 fi 88 89 NEED_LOAD=0 90 if [ "$STANDBY_ENDPOINT" != "$DESIRED_ENDPOINT" ]; then 91 NEED_LOAD=1 92 fi 93 94 echo "Mode:" 95 echo " $MODE" 96 echo 97 98 echo "Runtime:" 99 echo " active slot: $ACTIVE" 100 echo " active endpoint: $ACTIVE_ENDPOINT" 101 echo " active config: $ACTIVE_CONFIG" 102 echo " standby slot: $STANDBY" 103 echo " standby endpoint: $STANDBY_ENDPOINT" 104 echo " standby config: $STANDBY_CONFIG" 105 echo 106 107 echo "Selected:" 108 echo " best: $BEST_ENDPOINT | avg=$BEST_AVG | $BEST_FILE" 109 echo " second: $SECOND_ENDPOINT | avg=$SECOND_AVG | $SECOND_FILE" 110 echo 111 112 echo "Desired standby:" 113 echo " slot: $STANDBY" 114 echo " endpoint: $DESIRED_ENDPOINT" 115 echo " avg: $DESIRED_AVG" 116 echo " file: $DESIRED_FILE" 117 echo " config: $DESIRED_CONFIG" 118 echo " reason: $REASON" 119 echo 120 121 if [ "$NEED_LOAD" -eq 1 ]; then 122 echo "Plan:" 123 echo " load desired config into inactive slot $STANDBY" 124 echo " bring $STANDBY up" 125 echo " test ping through $STANDBY" 126 echo " if OK: keep active untouched and shut standby back down" 127 echo " if FAIL: quarantine desired endpoint for current batch and rerank" 128 else 129 echo "Plan:" 130 echo " standby already has desired endpoint" 131 echo " in standby-only mode: verify standby by bringing it up temporarily" 132 fi 133 134 echo 135 136 if [ "$MODE" = "dry-run" ]; then 137 echo "Dry-run only. No changes were made." 138 exit 0 139 fi 140 141 CRON_WAS_RUNNING=0 142 if /etc/init.d/cron status 2>/dev/null | grep -q running; then 143 CRON_WAS_RUNNING=1 144 fi 145 146 MAINT_WAS_PRESENT=0 147 if [ -e "$MAINTENANCE_FILE" ]; then 148 MAINT_WAS_PRESENT=1 149 fi 150 151 cleanup() { 152 ip route del "$PING_IP/32" dev "$STANDBY" 2>/dev/null || true 153 ifdown "$STANDBY" 2>/dev/null || true 154 155 if [ "$MAINT_WAS_PRESENT" -eq 0 ]; then 156 rm -f "$MAINTENANCE_FILE" 157 fi 158 159 rmdir /tmp/vpn-egress-hotplug-trigger.lock 2>/dev/null || true 160 rmdir /tmp/vpn-egress-manager.lock 2>/dev/null || true 161 162 if [ "$CRON_WAS_RUNNING" -eq 1 ]; then 163 /etc/init.d/cron start >/dev/null 2>&1 || true 164 fi 165 } 166 167 add_quarantine() { 168 mkdir -p /root/hmn/cache 169 chmod 700 /root/hmn/cache 170 171 if [ ! -f "$QUARANTINE" ]; then 172 printf 'endpoint\tfile\treason\tadded_at\n' > "$QUARANTINE" 173 fi 174 175 if awk -F '\t' -v ep="$DESIRED_ENDPOINT" -v f="$DESIRED_FILE" ' 176 NR > 1 && ($1 == ep || $2 == f) { found=1 } 177 END { exit found ? 0 : 1 } 178 ' "$QUARANTINE"; then 179 echo "Quarantine already contains this endpoint/file." 180 else 181 ADDED_AT="$(date -Iseconds 2>/dev/null || date)" 182 printf '%s\t%s\t%s\t%s\n' \ 183 "$DESIRED_ENDPOINT" \ 184 "$DESIRED_FILE" \ 185 "standby_apply_failed_current_batch" \ 186 "$ADDED_AT" >> "$QUARANTINE" 187 chmod 600 "$QUARANTINE" 188 echo "Added to quarantine:" 189 echo " $DESIRED_ENDPOINT | $DESIRED_FILE" 190 fi 191 } 192 193 trap cleanup EXIT INT TERM 194 195 echo "Freezing automation while applying standby..." 196 /etc/init.d/cron stop >/dev/null 2>&1 || true 197 touch "$MAINTENANCE_FILE" 198 rm -f /tmp/vpn-egress-skip-active /tmp/vpn-egress-skip-primary /tmp/vpn-egress-force-wan /root/vpn-egress-force-wan 199 rmdir /tmp/vpn-egress-hotplug-trigger.lock 2>/dev/null || true 200 rmdir /tmp/vpn-egress-manager.lock 2>/dev/null || true 201 202 if [ "$NEED_LOAD" -eq 1 ]; then 203 echo 204 echo "Loading desired config into standby slot..." 205 /root/hmn/hmn-load-vpn-slot.sh "$STANDBY" "$DESIRED_CONFIG" 206 else 207 echo 208 echo "No load needed; standby already points to desired endpoint." 209 fi 210 211 echo 212 echo "Bringing standby up for verification..." 213 ifup "$STANDBY" 214 sleep 12 215 216 echo 217 echo "Standby tunnel state:" 218 /usr/bin/amneziawg show "$STANDBY" 2>/dev/null || true 219 220 echo 221 echo "Temporary route for standby ping:" 222 ip route replace "$PING_IP/32" dev "$STANDBY" 223 ip route get "$PING_IP" 224 225 echo 226 echo "Ping through standby:" 227 if ping -4 -c 5 -W 2 -I "$STANDBY" "$PING_IP"; then 228 echo 229 echo "Standby verification OK." 230 else 231 echo 232 echo "ERROR: standby verification failed." 233 add_quarantine 234 echo 235 echo "Reranking after quarantine..." 236 /root/hmn/hmn-rank-awg.sh || true 237 exit 2 238 fi 239 240 echo 241 echo "Active slot was not changed:" 242 cat /root/hmn/state/active-slot 243 ip route show table 200 244 245 echo 246 echo "Done. Cleanup will shut standby down and restore automation." ### FILE /root/hmn/hmn-plan-selected.sh 1 #!/bin/ash 2 3 set -eu 4 5 SELECTED="${HMN_SELECTED_FILE:-/root/hmn/cache/selected-awg1-latest.tsv}" 6 7 if [ ! -f "$SELECTED" ]; then 8 echo "ERROR: selected file not found:" 9 echo " $SELECTED" 10 exit 1 11 fi 12 13 ACTIVE="$(cat /root/hmn/state/active-slot 2>/dev/null || true)" 14 15 case "$ACTIVE" in 16 vpn1) STANDBY="vpn2" ;; 17 vpn2) STANDBY="vpn1" ;; 18 *) 19 echo "ERROR: unknown active slot: $ACTIVE" 20 exit 1 21 ;; 22 esac 23 24 ACTIVE_ENDPOINT="$(uci -q get network.$ACTIVE.hmn_endpoint || true)" 25 STANDBY_ENDPOINT="$(uci -q get network.$STANDBY.hmn_endpoint || true)" 26 27 BEST_FILE="$(awk -F '\t' 'NR==2 {print $2}' "$SELECTED")" 28 BEST_ENDPOINT="$(awk -F '\t' 'NR==2 {print $3}' "$SELECTED")" 29 BEST_AVG="$(awk -F '\t' 'NR==2 {print $4}' "$SELECTED")" 30 BEST_CONFIG="$(awk -F '\t' 'NR==2 {print $5}' "$SELECTED")" 31 32 SECOND_FILE="$(awk -F '\t' 'NR==3 {print $2}' "$SELECTED")" 33 SECOND_ENDPOINT="$(awk -F '\t' 'NR==3 {print $3}' "$SELECTED")" 34 SECOND_AVG="$(awk -F '\t' 'NR==3 {print $4}' "$SELECTED")" 35 SECOND_CONFIG="$(awk -F '\t' 'NR==3 {print $5}' "$SELECTED")" 36 37 echo "Selected file:" 38 echo " $SELECTED" 39 echo 40 echo "Runtime:" 41 echo " active slot: $ACTIVE" 42 echo " active endpoint: $ACTIVE_ENDPOINT" 43 echo " standby slot: $STANDBY" 44 echo " standby endpoint: $STANDBY_ENDPOINT" 45 echo 46 echo "Selected candidates:" 47 echo " best: $BEST_ENDPOINT | $BEST_FILE | avg=$BEST_AVG" 48 echo " second: $SECOND_ENDPOINT | $SECOND_FILE | avg=$SECOND_AVG" 49 echo 50 echo "Plan:" 51 52 if [ "$ACTIVE_ENDPOINT" = "$BEST_ENDPOINT" ]; then 53 if [ "$STANDBY_ENDPOINT" = "$SECOND_ENDPOINT" ]; then 54 echo " Active slot already has best candidate." 55 echo " Standby slot already has second candidate." 56 echo " Action: do nothing." 57 else 58 echo " Active slot already has best candidate." 59 echo " Standby slot should be loaded with second candidate:" 60 echo " load $SECOND_CONFIG into $STANDBY" 61 echo " Action: load standby only, then test." 62 fi 63 elif [ "$ACTIVE_ENDPOINT" = "$SECOND_ENDPOINT" ]; then 64 if [ "$STANDBY_ENDPOINT" = "$BEST_ENDPOINT" ]; then 65 echo " Active slot has second candidate." 66 echo " Standby slot already has best candidate." 67 echo " Action: do nothing automatically; optional manual promote after policy decision." 68 else 69 echo " Active slot has second candidate." 70 echo " Standby slot should be loaded with best candidate:" 71 echo " load $BEST_CONFIG into $STANDBY" 72 echo " Action: load standby only, then test." 73 fi 74 else 75 echo " Active endpoint is not in top two selected candidates." 76 if [ "$STANDBY_ENDPOINT" = "$BEST_ENDPOINT" ]; then 77 echo " Standby slot already has best candidate." 78 echo " Action: do nothing automatically; optional manual promote after policy decision." 79 elif [ "$STANDBY_ENDPOINT" = "$SECOND_ENDPOINT" ]; then 80 echo " Standby slot has second candidate, but best candidate is available." 81 echo " Action: load best candidate into standby, then test:" 82 echo " load $BEST_CONFIG into $STANDBY" 83 else 84 echo " Standby slot should be loaded with best candidate:" 85 echo " load $BEST_CONFIG into $STANDBY" 86 echo " Action: load standby only, then test." 87 fi 88 fi 89 90 echo 91 echo "No changes were made." rc=0 ################################################################ ### LEGACY MANAGER RELEVANT CODE ################################################################ ### FILE /usr/bin/vpn-egress-manager.sh 3 TABLE="200" 7 SLOT_A="vpn1" 8 SLOT_B="vpn2" 16 STATE_DIR="/root/hmn/state" 18 ROUTE_STATE_FILE="/tmp/vpn-egress-current.state" 20 FORCE_WAN_FILE="/tmp/vpn-egress-force-wan" 21 FORCE_WAN_PERSISTENT_FILE="/root/vpn-egress-force-wan" 24 SKIP_PRIMARY_FILE="/tmp/vpn-egress-skip-primary" 26 MAINTENANCE_FILE="/tmp/hmn-vpn-maintenance" 29 # HMN_VPN_USER_OVERRIDE_MANAGER_GUARD_V1 30 # If timed vpn_user override is active, do not let the normal egress manager 31 # overwrite table 200 back to vpn1/vpn2. 32 if [ -x /root/hmn/hmn-vpn-user-override.sh ]; then 33 if /root/hmn/hmn-vpn-user-override.sh manager-guard >/dev/null 2>&1; then 38 # END_HMN_VPN_USER_OVERRIDE_MANAGER_GUARD_V1 39 REFRESH_LOCK="/tmp/hmn-refresh-pool-safe.lock" 44 ensure_rules() { 45 ip rule del iif "$VPNIN_DEV" lookup "$TABLE" 2>/dev/null 46 ip rule del from "$VPNIN_NET" lookup "$TABLE" 2>/dev/null 48 ip rule add iif "$VPNIN_DEV" lookup "$TABLE" pref 10019 49 ip rule add from "$VPNIN_NET" lookup "$TABLE" pref 10020 58 iface_exists "$DEV" || ifup "$DEV" >/dev/null 2>&1 69 get_table_active() { 70 ip route show table "$TABLE" | awk '/^default dev /{print $3; exit}' 82 TABLE_ACTIVE="$(get_table_active)" 83 case "$TABLE_ACTIVE" in 85 echo "$TABLE_ACTIVE" 99 ip route del "$PROBE_IP1/32" dev "$DEV" 2>/dev/null || true 100 ip route del "$PROBE_IP2/32" dev "$DEV" 2>/dev/null || true 120 ip route replace "$IP/32" dev "$DEV" 2>/dev/null 123 logger -t "${LOGTAG:-vpn-egress}" "strict health failed for $DEV: cannot add probe route to $IP" 142 ROUTE="$(ip route show table "$TABLE" | sed -n '1p')" 143 MAIN_DEFAULT="$(ip -4 route show default | sed -n '1p')" 144 CURRENT="$TARGET | $ROUTE" 145 LAST="$(cat "$ROUTE_STATE_FILE" 2>/dev/null)" 153 echo "route: $ROUTE" 167 echo "$CURRENT" > "$ROUTE_STATE_FILE" 174 ip route replace default dev "$DEV" table "$TABLE" 175 /usr/bin/vpn-table200-local-routes.sh || true 176 ip route flush cache 183 MAIN_DEFAULT="$(ip -4 route show default | sed -n '1p')" 193 WAN_GW="$(ubus call network.interface.wan status 2>/dev/null | jsonfilter -e '@.route[@.target="0.0.0.0"].nexthop' 2>/dev/null | head -n1)" 199 ip route replace default via "$WAN_GW" dev "$WAN_DEV" table "$TABLE" 201 ip route replace default dev "$WAN_DEV" table "$TABLE" 204 /usr/bin/vpn-table200-local-routes.sh || true 205 ip route flush cache 206 log_change "WAN fallback" 214 ifdown "$DEV" >/dev/null 2>&1 || true 226 REFILL_HELPER="/root/hmn/hmn-refill-slot.sh" 227 REFILL_LOG="/root/hmn/refill.log" 253 ip route replace default dev "$ACTIVE_NOW" table "$TABLE" 2>/dev/null || true 254 /usr/bin/vpn-table200-local-routes.sh || true 255 ip route flush cache 263 ifup "$DEV" >/dev/null 2>&1 || true 273 ifdown "$DEV" >/dev/null 2>&1 || true 282 if [ "${HMN_ALLOW_MANAGER_DURING_REFRESH:-0}" != "1" ] && [ -d "$REFRESH_LOCK" ]; then 294 ensure_rules 296 if [ -e "$FORCE_WAN_FILE" ] || [ -e "$FORCE_WAN_PERSISTENT_FILE" ]; then 297 logger -t "$LOGTAG" "force WAN flag present, using WAN fallback" 308 if [ -e "$SKIP_ACTIVE_FILE" ] || [ -e "$SKIP_PRIMARY_FILE" ]; then 321 logger -t "$LOGTAG" "active slot $ACTIVE failed health, forced restart before standby" 323 ifdown "$ACTIVE" >/dev/null 2>&1 || true 325 ifup "$ACTIVE" >/dev/null 2>&1 || true 329 logger -t "$LOGTAG" "active slot $ACTIVE recovered after forced restart" 335 logger -t "$LOGTAG" "active slot $ACTIVE still failed after forced restart" 351 # keep old active instead of falling through to WAN fallback. 352 logger -t "$LOGTAG" "standby $STANDBY failed, re-checking old active $ACTIVE before WAN fallback" rc=0 ################################################################ ### LOADER MECHANISM GREP ################################################################ === uci/ifup/ifdown/wg parsing lines === /root/hmn/hmn-load-vpn-test.sh:36:PRIVATE_KEY=[REDACTED_PRIVATE_KEY] PrivateKey)" /root/hmn/hmn-load-vpn-test.sh:37:ADDRESS="$(getv Address)" /root/hmn/hmn-load-vpn-test.sh:39:JC="$(getv Jc)" /root/hmn/hmn-load-vpn-test.sh:40:JMIN="$(getv Jmin)" /root/hmn/hmn-load-vpn-test.sh:41:JMAX="$(getv Jmax)" /root/hmn/hmn-load-vpn-test.sh:42:S1="$(getv S1)" /root/hmn/hmn-load-vpn-test.sh:43:S2="$(getv S2)" /root/hmn/hmn-load-vpn-test.sh:44:H1="$(getv H1)" /root/hmn/hmn-load-vpn-test.sh:45:H2="$(getv H2)" /root/hmn/hmn-load-vpn-test.sh:46:H3="$(getv H3)" /root/hmn/hmn-load-vpn-test.sh:47:H4="$(getv H4)" /root/hmn/hmn-load-vpn-test.sh:49:PUBLIC_KEY=[REDACTED_PUBLIC_KEY] PublicKey)" /root/hmn/hmn-load-vpn-test.sh:50:ALLOWED_IPS="$(getv AllowedIPs)" /root/hmn/hmn-load-vpn-test.sh:51:ENDPOINT="$(getv Endpoint)" /root/hmn/hmn-load-vpn-test.sh:52:KEEPALIVE="$(getv PersistentKeepalive)" /root/hmn/hmn-load-vpn-test.sh:54:ENDPOINT_HOST="${ENDPOINT%:*}" /root/hmn/hmn-load-vpn-test.sh:55:ENDPOINT_PORT="${ENDPOINT##*:}" /root/hmn/hmn-load-vpn-test.sh:57:if [ -z "$PRIVATE_KEY" ] || [ -z "$ADDRESS" ] || [ -z "$PUBLIC_KEY" ] || [ -z "$ENDPOINT_HOST" ] || [ -z "$ENDPOINT_PORT" ]; then /root/hmn/hmn-load-vpn-test.sh:61: echo "Address=$ADDRESS" /root/hmn/hmn-load-vpn-test.sh:62: echo "PrivateKey=[REDACTED_PRIVATE_KEY] /root/hmn/hmn-load-vpn-test.sh:63: echo "PublicKey=[REDACTED_PUBLIC_KEY] /root/hmn/hmn-load-vpn-test.sh:64: echo "Endpoint=$ENDPOINT" /root/hmn/hmn-load-vpn-test.sh:84:echo " Address: $ADDRESS" /root/hmn/hmn-load-vpn-test.sh:85:echo " Endpoint: $ENDPOINT" /root/hmn/hmn-load-vpn-test.sh:87:echo " AWG: Jc=$JC Jmin=$JMIN Jmax=$JMAX S1=$S1 S2=$S2" /root/hmn/hmn-load-vpn-test.sh:90:ifdown vpn_test 2>/dev/null || true /root/hmn/hmn-load-vpn-test.sh:95: SEC="$(uci -q show network | sed -n 's/^\(network\.@amneziawg_vpn_test\[[0-9][0-9]*\]\)=amneziawg_vpn_test$/\1/p' | head -n 1)" /root/hmn/hmn-load-vpn-test.sh:100:uci set network.vpn_test='interface' /root/hmn/hmn-load-vpn-test.sh:101:uci set network.vpn_test.proto='amneziawg' /root/hmn/hmn-load-vpn-test.sh:102:uci set network.vpn_test.private_key=[REDACTED_PRIVATE_KEY] /root/hmn/hmn-load-vpn-test.sh:103:uci set network.vpn_test.awg_jc="$JC" /root/hmn/hmn-load-vpn-test.sh:104:uci set network.vpn_test.awg_jmin="$JMIN" /root/hmn/hmn-load-vpn-test.sh:105:uci set network.vpn_test.awg_jmax="$JMAX" /root/hmn/hmn-load-vpn-test.sh:106:uci set network.vpn_test.awg_s1="$S1" /root/hmn/hmn-load-vpn-test.sh:107:uci set network.vpn_test.awg_s2="$S2" /root/hmn/hmn-load-vpn-test.sh:108:uci set network.vpn_test.awg_h1="$H1" /root/hmn/hmn-load-vpn-test.sh:109:uci set network.vpn_test.awg_h2="$H2" /root/hmn/hmn-load-vpn-test.sh:110:uci set network.vpn_test.awg_h3="$H3" /root/hmn/hmn-load-vpn-test.sh:111:uci set network.vpn_test.awg_h4="$H4" /root/hmn/hmn-load-vpn-test.sh:113:uci set network.vpn_test.auto='0' /root/hmn/hmn-load-vpn-test.sh:114:uci set network.vpn_test.disabled='0' /root/hmn/hmn-load-vpn-test.sh:115:uci set network.vpn_test.delegate='0' /root/hmn/hmn-load-vpn-test.sh:116:uci set network.vpn_test.peerdns='0' /root/hmn/hmn-load-vpn-test.sh:117:uci set network.vpn_test.defaultroute='0' /root/hmn/hmn-load-vpn-test.sh:119:uci add_list network.vpn_test.addresses="$ADDRESS" /root/hmn/hmn-load-vpn-test.sh:126: [ -n "$D" ] && uci add_list network.vpn_test.dns="$D" /root/hmn/hmn-load-vpn-test.sh:131:PEER="$(uci add network amneziawg_vpn_test)" /root/hmn/hmn-load-vpn-test.sh:132:uci set network."$PEER".description="$(basename "$CONF")" /root/hmn/hmn-load-vpn-test.sh:133:uci set network."$PEER".public_key=[REDACTED_PUBLIC_KEY] /root/hmn/hmn-load-vpn-test.sh:139: [ -n "$A" ] && uci add_list network."$PEER".allowed_ips="$A" /root/hmn/hmn-load-vpn-test.sh:143:uci set network."$PEER".route_allowed_ips='0' /root/hmn/hmn-load-vpn-test.sh:144:uci set network."$PEER".persistent_keepalive="${KEEPALIVE:-25}" /root/hmn/hmn-load-vpn-test.sh:145:uci set network."$PEER".endpoint_host="$ENDPOINT_HOST" /root/hmn/hmn-load-vpn-test.sh:146:uci set network."$PEER".endpoint_port="$ENDPOINT_PORT" /root/hmn/hmn-load-vpn-test.sh:148:uci set network.vpn_test.hmn_role='test_slot' /root/hmn/hmn-load-vpn-test.sh:149:uci set network.vpn_test.hmn_source_config="$CONF" /root/hmn/hmn-load-vpn-test.sh:150:uci set network.vpn_test.hmn_loaded_at="$(date -Iseconds)" /root/hmn/hmn-load-vpn-test.sh:151:uci set network.vpn_test.hmn_endpoint="$ENDPOINT" /root/hmn/hmn-load-vpn-test.sh:153:uci commit network /root/hmn/hmn-load-vpn-test.sh:162:uci show network | grep -E '=amneziawg_vpn_test|@amneziawg_vpn_test.*public_key|@amneziawg_vpn_test.*allowed_ips|@amneziawg_vpn_test.*route_allowed_ips|@amneziawg_vpn_test.*endpoint_host|@amneziawg_vpn_test.*endpoint_port|@amneziawg_vpn_test.*persistent_keepalive' /root/hmn/hmn-load-vpn-test.sh:171: ifup vpn_test /root/hmn/hmn-load-vpn-test.sh:179: echo "AWG/WG show:" /root/hmn/hmn-load-vpn-test.sh:180: awg show vpn_test 2>/dev/null || wg show vpn_test 2>/dev/null || true /root/hmn/hmn-load-vpn-test.sh:183: echo "Table 200 after ifup:" /root/hmn/hmn-load-vpn-test.sh:188: echo " ifup vpn_test" /root/hmn/hmn-load-vpn-user.sh:36:PRIVATE_KEY=[REDACTED_PRIVATE_KEY] PrivateKey)" /root/hmn/hmn-load-vpn-user.sh:37:ADDRESS="$(getv Address)" /root/hmn/hmn-load-vpn-user.sh:39:JC="$(getv Jc)" /root/hmn/hmn-load-vpn-user.sh:40:JMIN="$(getv Jmin)" /root/hmn/hmn-load-vpn-user.sh:41:JMAX="$(getv Jmax)" /root/hmn/hmn-load-vpn-user.sh:42:S1="$(getv S1)" /root/hmn/hmn-load-vpn-user.sh:43:S2="$(getv S2)" /root/hmn/hmn-load-vpn-user.sh:44:H1="$(getv H1)" /root/hmn/hmn-load-vpn-user.sh:45:H2="$(getv H2)" /root/hmn/hmn-load-vpn-user.sh:46:H3="$(getv H3)" /root/hmn/hmn-load-vpn-user.sh:47:H4="$(getv H4)" /root/hmn/hmn-load-vpn-user.sh:49:PUBLIC_KEY=[REDACTED_PUBLIC_KEY] PublicKey)" /root/hmn/hmn-load-vpn-user.sh:50:ALLOWED_IPS="$(getv AllowedIPs)" /root/hmn/hmn-load-vpn-user.sh:51:ENDPOINT="$(getv Endpoint)" /root/hmn/hmn-load-vpn-user.sh:52:KEEPALIVE="$(getv PersistentKeepalive)" /root/hmn/hmn-load-vpn-user.sh:54:ENDPOINT_HOST="${ENDPOINT%:*}" /root/hmn/hmn-load-vpn-user.sh:55:ENDPOINT_PORT="${ENDPOINT##*:}" /root/hmn/hmn-load-vpn-user.sh:57:if [ -z "$PRIVATE_KEY" ] || [ -z "$ADDRESS" ] || [ -z "$PUBLIC_KEY" ] || [ -z "$ENDPOINT_HOST" ] || [ -z "$ENDPOINT_PORT" ]; then /root/hmn/hmn-load-vpn-user.sh:61: echo "Address=$ADDRESS" /root/hmn/hmn-load-vpn-user.sh:62: echo "PrivateKey=[REDACTED_PRIVATE_KEY] /root/hmn/hmn-load-vpn-user.sh:63: echo "PublicKey=[REDACTED_PUBLIC_KEY] /root/hmn/hmn-load-vpn-user.sh:64: echo "Endpoint=$ENDPOINT" /root/hmn/hmn-load-vpn-user.sh:71:# Do not load the same AWG endpoint/config into vpn_user if it is already present /root/hmn/hmn-load-vpn-user.sh:75: TARGET_EP="${ENDPOINT_HOST}:${ENDPOINT_PORT}" /root/hmn/hmn-load-vpn-user.sh:91: # Compare by peer endpoint in amneziawg slot section. /root/hmn/hmn-load-vpn-user.sh:92: SEC="$(uci -q show network | sed -n "s/^\(network\.@amneziawg_${SLOT}\[[0-9][0-9]*\]\)=amneziawg_${SLOT}$/\1/p" | head -n 1)" /root/hmn/hmn-load-vpn-user.sh:94: EH="$(uci -q get ${SEC}.endpoint_host || true)" /root/hmn/hmn-load-vpn-user.sh:95: EP="$(uci -q get ${SEC}.endpoint_port || true)" /root/hmn/hmn-load-vpn-user.sh:97: echo "ERROR: selected endpoint already loaded in $SLOT: $TARGET_EP" /root/hmn/hmn-load-vpn-user.sh:121:echo " Address: $ADDRESS" /root/hmn/hmn-load-vpn-user.sh:122:echo " Endpoint: $ENDPOINT" /root/hmn/hmn-load-vpn-user.sh:124:echo " AWG: Jc=$JC Jmin=$JMIN Jmax=$JMAX S1=$S1 S2=$S2" /root/hmn/hmn-load-vpn-user.sh:127:ifdown vpn_user 2>/dev/null || true /root/hmn/hmn-load-vpn-user.sh:132: SEC="$(uci -q show network | sed -n 's/^\(network\.@amneziawg_vpn_user\[[0-9][0-9]*\]\)=amneziawg_vpn_user$/\1/p' | head -n 1)" /root/hmn/hmn-load-vpn-user.sh:137:uci set network.vpn_user='interface' /root/hmn/hmn-load-vpn-user.sh:138:uci set network.vpn_user.proto='amneziawg' /root/hmn/hmn-load-vpn-user.sh:139:uci set network.vpn_user.private_key=[REDACTED_PRIVATE_KEY] /root/hmn/hmn-load-vpn-user.sh:140:uci set network.vpn_user.awg_jc="$JC" /root/hmn/hmn-load-vpn-user.sh:141:uci set network.vpn_user.awg_jmin="$JMIN" /root/hmn/hmn-load-vpn-user.sh:142:uci set network.vpn_user.awg_jmax="$JMAX" /root/hmn/hmn-load-vpn-user.sh:143:uci set network.vpn_user.awg_s1="$S1" /root/hmn/hmn-load-vpn-user.sh:144:uci set network.vpn_user.awg_s2="$S2" /root/hmn/hmn-load-vpn-user.sh:145:uci set network.vpn_user.awg_h1="$H1" /root/hmn/hmn-load-vpn-user.sh:146:uci set network.vpn_user.awg_h2="$H2" /root/hmn/hmn-load-vpn-user.sh:147:uci set network.vpn_user.awg_h3="$H3" /root/hmn/hmn-load-vpn-user.sh:148:uci set network.vpn_user.awg_h4="$H4" /root/hmn/hmn-load-vpn-user.sh:150:uci set network.vpn_user.auto='0' /root/hmn/hmn-load-vpn-user.sh:151:uci set network.vpn_user.disabled='0' /root/hmn/hmn-load-vpn-user.sh:152:uci set network.vpn_user.delegate='0' /root/hmn/hmn-load-vpn-user.sh:153:uci set network.vpn_user.peerdns='0' /root/hmn/hmn-load-vpn-user.sh:154:uci set network.vpn_user.defaultroute='0' /root/hmn/hmn-load-vpn-user.sh:156:uci add_list network.vpn_user.addresses="$ADDRESS" /root/hmn/hmn-load-vpn-user.sh:163: [ -n "$D" ] && uci add_list network.vpn_user.dns="$D" /root/hmn/hmn-load-vpn-user.sh:168:PEER="$(uci add network amneziawg_vpn_user)" /root/hmn/hmn-load-vpn-user.sh:169:uci set network."$PEER".description="$(basename "$CONF")" /root/hmn/hmn-load-vpn-user.sh:170:uci set network."$PEER".public_key=[REDACTED_PUBLIC_KEY] /root/hmn/hmn-load-vpn-user.sh:176: [ -n "$A" ] && uci add_list network."$PEER".allowed_ips="$A" /root/hmn/hmn-load-vpn-user.sh:180:uci set network."$PEER".route_allowed_ips='0' /root/hmn/hmn-load-vpn-user.sh:181:uci set network."$PEER".persistent_keepalive="${KEEPALIVE:-25}" /root/hmn/hmn-load-vpn-user.sh:182:uci set network."$PEER".endpoint_host="$ENDPOINT_HOST" /root/hmn/hmn-load-vpn-user.sh:183:uci set network."$PEER".endpoint_port="$ENDPOINT_PORT" /root/hmn/hmn-load-vpn-user.sh:185:uci set network.vpn_user.hmn_role='user_select_slot' /root/hmn/hmn-load-vpn-user.sh:186:uci set network.vpn_user.hmn_source_config="$CONF" /root/hmn/hmn-load-vpn-user.sh:187:uci set network.vpn_user.hmn_loaded_at="$(date -Iseconds)" /root/hmn/hmn-load-vpn-user.sh:188:uci set network.vpn_user.hmn_endpoint="$ENDPOINT" /root/hmn/hmn-load-vpn-user.sh:190:uci commit network /root/hmn/hmn-load-vpn-user.sh:199:echo " address=$(uci -q get network.vpn_user.addresses || true)" /root/hmn/hmn-load-vpn-user.sh:202:echo " endpoint=$(uci -q get network.vpn_user.hmn_endpoint || true)" /root/hmn/hmn-load-vpn-user.sh:203:PEER_SUM="$(uci -q show network | sed -n 's/^\(network\.@amneziawg_vpn_user\[[0-9][0-9]*\]\)=amneziawg_vpn_user$/\1/p' | head -n 1)" /root/hmn/hmn-load-vpn-user.sh:209: echo " peer_endpoint=$(uci -q get ${PEER_SUM}.endpoint_host || true):$(uci -q get ${PEER_SUM}.endpoint_port || true)" /root/hmn/hmn-load-vpn-user.sh:219: ifup vpn_user /root/hmn/hmn-load-vpn-user.sh:227: echo "AWG/WG show: hidden in normal output; use explicit admin diagnostics if needed" /root/hmn/hmn-load-vpn-user.sh:230: echo "Table 200 after ifup:" /root/hmn/hmn-load-vpn-user.sh:235: echo " ifup vpn_user" /root/hmn/hmn-refill-slot.sh:42: date +"/root/hmn/state/bad-endpoints-%Y%m%d.txt" /root/hmn/hmn-refill-slot.sh:47: H="$(uci -q get network.@amneziawg_${S}[0].endpoint_host || true)" /root/hmn/hmn-refill-slot.sh:48: P="$(uci -q get network.@amneziawg_${S}[0].endpoint_port || true)" /root/hmn/hmn-refill-slot.sh:138: echo "bad endpoints today:" /root/hmn/hmn-refill-slot.sh:165: echo "bad endpoints today:" /root/hmn/hmn-refill-slot.sh:172:ifdown "$SLOT" 2>/dev/null || true /root/hmn/hmn-refill-slot.sh:184:ifup "$ACTIVE_SLOT" 2>/dev/null || true /root/hmn/hmn-refill-slot.sh:194:ifup "$SLOT" /root/hmn/hmn-refill-slot.sh:199:/usr/bin/amneziawg show "$SLOT" 2>/dev/null | sed -n '1,35p' || true /root/hmn/hmn-refill-slot.sh:202: echo "ERROR: $SLOT link absent after ifup" /root/hmn/hmn-refill-slot.sh:210: ifdown "$SLOT" 2>/dev/null || true /root/hmn/hmn-refill-slot.sh:222: printf "slot\tfile\tendpoint\tavg_ms\tconfig_path\n" /root/hmn/hmn-refill-slot.sh:243:ifdown "$SLOT" 2>/dev/null || true /root/hmn/hmn-apply-selected.sh:40:ACTIVE_ENDPOINT="$(uci -q get network.$ACTIVE.hmn_endpoint || true)" /root/hmn/hmn-apply-selected.sh:43:STANDBY_ENDPOINT="$(uci -q get network.$STANDBY.hmn_endpoint || true)" /root/hmn/hmn-apply-selected.sh:47:BEST_ENDPOINT="$(awk -F '\t' 'NR==2 {print $3}' "$SELECTED")" /root/hmn/hmn-apply-selected.sh:52:SECOND_ENDPOINT="$(awk -F '\t' 'NR==3 {print $3}' "$SELECTED")" /root/hmn/hmn-apply-selected.sh:56:if [ -z "$BEST_ENDPOINT" ] || [ -z "$BEST_CONFIG" ] || [ -z "$SECOND_ENDPOINT" ] || [ -z "$SECOND_CONFIG" ]; then /root/hmn/hmn-apply-selected.sh:63:if [ "$ACTIVE_ENDPOINT" = "$BEST_ENDPOINT" ]; then /root/hmn/hmn-apply-selected.sh:65: DESIRED_ENDPOINT="$SECOND_ENDPOINT" /root/hmn/hmn-apply-selected.sh:69:elif [ "$ACTIVE_ENDPOINT" = "$SECOND_ENDPOINT" ]; then /root/hmn/hmn-apply-selected.sh:71: DESIRED_ENDPOINT="$BEST_ENDPOINT" /root/hmn/hmn-apply-selected.sh:77: DESIRED_ENDPOINT="$BEST_ENDPOINT" /root/hmn/hmn-apply-selected.sh:90:if [ "$STANDBY_ENDPOINT" != "$DESIRED_ENDPOINT" ]; then /root/hmn/hmn-apply-selected.sh:100:echo " active endpoint: $ACTIVE_ENDPOINT" /root/hmn/hmn-apply-selected.sh:103:echo " standby endpoint: $STANDBY_ENDPOINT" /root/hmn/hmn-apply-selected.sh:108:echo " best: $BEST_ENDPOINT | avg=$BEST_AVG | $BEST_FILE" /root/hmn/hmn-apply-selected.sh:109:echo " second: $SECOND_ENDPOINT | avg=$SECOND_AVG | $SECOND_FILE" /root/hmn/hmn-apply-selected.sh:114:echo " endpoint: $DESIRED_ENDPOINT" /root/hmn/hmn-apply-selected.sh:127: echo " if FAIL: quarantine desired endpoint for current batch and rerank" /root/hmn/hmn-apply-selected.sh:130: echo " standby already has desired endpoint" /root/hmn/hmn-apply-selected.sh:153: ifdown "$STANDBY" 2>/dev/null || true /root/hmn/hmn-apply-selected.sh:172: printf 'endpoint\tfile\treason\tadded_at\n' > "$QUARANTINE" /root/hmn/hmn-apply-selected.sh:175: if awk -F '\t' -v ep="$DESIRED_ENDPOINT" -v f="$DESIRED_FILE" ' /root/hmn/hmn-apply-selected.sh:179: echo "Quarantine already contains this endpoint/file." /root/hmn/hmn-apply-selected.sh:183: "$DESIRED_ENDPOINT" \ /root/hmn/hmn-apply-selected.sh:189: echo " $DESIRED_ENDPOINT | $DESIRED_FILE" /root/hmn/hmn-apply-selected.sh:208: echo "No load needed; standby already points to desired endpoint." /root/hmn/hmn-apply-selected.sh:213:ifup "$STANDBY" /root/hmn/hmn-apply-selected.sh:218:/usr/bin/amneziawg show "$STANDBY" 2>/dev/null || true /root/hmn/hmn-plan-selected.sh:24:ACTIVE_ENDPOINT="$(uci -q get network.$ACTIVE.hmn_endpoint || true)" /root/hmn/hmn-plan-selected.sh:25:STANDBY_ENDPOINT="$(uci -q get network.$STANDBY.hmn_endpoint || true)" /root/hmn/hmn-plan-selected.sh:28:BEST_ENDPOINT="$(awk -F '\t' 'NR==2 {print $3}' "$SELECTED")" /root/hmn/hmn-plan-selected.sh:33:SECOND_ENDPOINT="$(awk -F '\t' 'NR==3 {print $3}' "$SELECTED")" /root/hmn/hmn-plan-selected.sh:42:echo " active endpoint: $ACTIVE_ENDPOINT" /root/hmn/hmn-plan-selected.sh:44:echo " standby endpoint: $STANDBY_ENDPOINT" /root/hmn/hmn-plan-selected.sh:47:echo " best: $BEST_ENDPOINT | $BEST_FILE | avg=$BEST_AVG" /root/hmn/hmn-plan-selected.sh:48:echo " second: $SECOND_ENDPOINT | $SECOND_FILE | avg=$SECOND_AVG" /root/hmn/hmn-plan-selected.sh:52:if [ "$ACTIVE_ENDPOINT" = "$BEST_ENDPOINT" ]; then /root/hmn/hmn-plan-selected.sh:53: if [ "$STANDBY_ENDPOINT" = "$SECOND_ENDPOINT" ]; then /root/hmn/hmn-plan-selected.sh:63:elif [ "$ACTIVE_ENDPOINT" = "$SECOND_ENDPOINT" ]; then /root/hmn/hmn-plan-selected.sh:64: if [ "$STANDBY_ENDPOINT" = "$BEST_ENDPOINT" ]; then /root/hmn/hmn-plan-selected.sh:75: echo " Active endpoint is not in top two selected candidates." /root/hmn/hmn-plan-selected.sh:76: if [ "$STANDBY_ENDPOINT" = "$BEST_ENDPOINT" ]; then /root/hmn/hmn-plan-selected.sh:79: elif [ "$STANDBY_ENDPOINT" = "$SECOND_ENDPOINT" ]; then /usr/bin/vpn-egress-manager.sh:4:VPNIN_DEV="eth1" /usr/bin/vpn-egress-manager.sh:58: iface_exists "$DEV" || ifup "$DEV" >/dev/null 2>&1 /usr/bin/vpn-egress-manager.sh:156: for IF in "$SLOT_A" "$SLOT_B" vpn_test eth0 eth1 eth3; do /usr/bin/vpn-egress-manager.sh:214: ifdown "$DEV" >/dev/null 2>&1 || true /usr/bin/vpn-egress-manager.sh:263: ifup "$DEV" >/dev/null 2>&1 || true /usr/bin/vpn-egress-manager.sh:273: ifdown "$DEV" >/dev/null 2>&1 || true /usr/bin/vpn-egress-manager.sh:323: ifdown "$ACTIVE" >/dev/null 2>&1 || true /usr/bin/vpn-egress-manager.sh:325: ifup "$ACTIVE" >/dev/null 2>&1 || true rc=0 ################################################################ ### CURRENT SAFE STATE ################################################################ === interfaces === lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 95.165.105.4/20 fe80::be24:11ff:fe42:83cf/64 eth1 UP 10.200.0.2/24 fe80::be24:11ff:fe09:4e2c/64 eth2 UP 10.71.100.2/24 fe80::be24:11ff:fe4d:7a71/64 eth3 UP 10.201.0.2/24 fe80::be24:11ff:fec8:2ee1/64 vpn1 UNKNOWN 10.91.233.132/32 === rules === 0: from all lookup local 10019: from all iif eth1 lookup 200 10020: from 10.200.0.0/24 lookup 200 32766: from all lookup main 32767: from all lookup default === tables 200-205 === TABLE 200 default dev vpn1 scope link 10.200.0.0/24 dev eth1 scope link src 10.200.0.2 10.201.0.0/24 dev eth3 scope link src 10.201.0.2 TABLE 201 TABLE 202 TABLE 203 TABLE 204 TABLE 205 === firewall vpn_out === firewall.@zone[3].name='vpn_in' firewall.@zone[3].network='vpn_in' firewall.@zone[4].name='vpn_out' firewall.@zone[4].network='vpn1' 'vpn2' 'vpn_user' firewall.@rule[7].name='Allow-ICMP-from-vpn_in-to-router' firewall.@rule[7].src='vpn_in' firewall.@forwarding[0]=forwarding firewall.@forwarding[0].src='direct_in' firewall.@forwarding[0].dest='wan' firewall.@forwarding[1]=forwarding firewall.@forwarding[1].src='vpn_in' firewall.@forwarding[1].dest='vpn_out' firewall.@forwarding[2]=forwarding firewall.@forwarding[2].src='vpn_in' firewall.@forwarding[2].dest='wan' === network safe fields === ### network.vpn1 proto=amneziawg auto=0 disabled=0 addresses=10.91.233.132/32 hmn_role=active_spare_slot hmn_endpoint=178.215.227.13:44408 hmn_loaded_at=2026-07-06T17:07:53+00:00 ### network.vpn2 proto=amneziawg auto=0 disabled=0 addresses=10.86.108.200/32 hmn_role=active_spare_slot hmn_endpoint=192.121.163.235:42530 hmn_loaded_at=2026-07-08T06:15:47+00:00 ### network.vpn3 ### network.vpn4 ### network.vpn5 ### network.vpn_user proto=amneziawg auto=0 disabled=0 addresses=10.90.235.244/32 hmn_role=user_select_slot hmn_endpoint=37.235.54.96:38971 hmn_loaded_at=2026-07-06T17:41:02+00:00 ### network.vpn_test proto=amneziawg auto=0 disabled=0 addresses=10.67.234.5/32 hmn_role=test_slot hmn_endpoint=192.71.27.64:40044 hmn_loaded_at=2026-07-08T04:31:40+00:00 rc=1 ################################################################ ### ASSESSMENT ################################################################ No changes applied. This second inspect avoids nl and should include the actual sanitized loader code.