steer: туннели в warp.up лучшим первым, запасной порт при привязке точки, без мёртвых функций

warp.up писался в порядке номеров туннелей, хотя комментарий обещал «лучший первым», а порядок
читают все, кому нужен один туннель: _st_warp_first, мост Telegram (warp.lst — первый живой) и
движок (devices в spec решают ничьи в пределах допуска prefer: latency). Разведка теперь отдаёт
ключ качества (разряд запаса, потери, круг), и _st_warp_order ставит туннели по нему. Если ни
один туннель не разведан заново, порядок прежний: перестановка по дрожанию пинга меняла бы spec
и перезапускала движок на каждом «Применить».

Второй найденный порт из warp.ports стал запасным: не дала точка рукопожатия при окончательной
привязке — тот же адрес пробуется на нём, прежде чем туннель сдаётся.

_st_warp_handshake, _st_warp_hs_epoch и _st_warp_colo нигде не вызывались — убраны.
This commit is contained in:
xyzmean committed 2026-09-24 02:54:40 +03:00
1 parent 404ba5f7db
commit 069d93f463
1 file changed
+81 -24
+81 -24
View File
@@ -4847,7 +4847,7 @@ ST_CRON_CMD="/etc/init.d/steer enabled && { for i in zmwarp zmwarp2 zmwarp3; do
# страница, его ключи и точка переживают обновление. У каждого своя регистрация: одни ключи
# на трёх устройствах Cloudflare считает одним клиентом.
ST_WARP_N=3
ST_WARP_UP="$ST_DIR/warp.up" # поднятые туннели «интерфейс колония», лучший первым
ST_WARP_UP="$ST_DIR/warp.up" # поднятые туннели «интерфейс колония», лучший первым (_st_warp_order)
# Российские колонии Cloudflare: выход у них внутри страны, за теми же ТСПУ, что и без туннеля, —
# через них не снимается ни геоблок, ни остальные блокировки. Берутся только в запас.
ST_RU_COLOS="DME SVX LED KJA REN OVB KZN AER VVO"
@@ -5140,19 +5140,6 @@ _st_warp_zone() {
fi
}
_st_warp_hs_epoch() { awg show "$ST_WARP_IF" latest-handshakes 2>/dev/null | awk '{print $2}' | head -n1; }
_st_warp_handshake() { # ждать рукопожатия до 10 с
local i=0 hs
while [ "$i" -lt 10 ]; do
hs=$(_st_warp_hs_epoch)
[ -n "$hs" ] && [ "$hs" != 0 ] && return 0
ping -c1 -W1 -I "$ST_WARP_IF" 1.1.1.1 >/dev/null 2>&1
i=$((i + 1))
done
return 1
}
_st_warp_is_ru() { case " $ST_RU_COLOS " in *" $1 "*) return 0 ;; esac; return 1; }
# ── несколько туннелей ──
@@ -5229,9 +5216,9 @@ _st_colo_of() { # ИНТЕРФЕЙС
sed -n 's/^colo=//p' | head -n1)
[ -n "$c" ] && echo "$c"
}
_st_warp_colo() { _st_colo_of "$ST_WARP_IF"; }
# Какие порты провайдер выпускает к WARP — один раз, дальше из файла.
# Какие порты провайдер выпускает к WARP — один раз, дальше из файла. Ищутся два: разведка
# ведёт все адреса первым, второй — запасной при окончательной привязке точки (_st_warp_up).
_st_warp_ports() { # ИНТЕРФЕЙС КЛЮЧ_УЗЛА АДРЕС
local f="$ST_DIR/warp.ports" ok="" p
[ -s "$f" ] && { cat "$f"; return 0; }
@@ -5245,10 +5232,16 @@ _st_warp_ports() { # ИНТЕРФЕЙС КЛЮЧ_УЗЛА АДРЕС
echo "$ok"
}
# Разведка для одного туннеля. Печатает «адрес порт колония» лучшей точки. Занятая колония —
# запас (другой адрес в той же колонии лучше никакого), российская — последний запас.
# Разведка для одного туннеля. Печатает «адрес порт колония ключ» лучшей точки. Занятая
# колония — запас (другой адрес в той же колонии лучше никакого), российская — последний запас.
#
# Ключ — качество точки одним числом, меньше — лучше: разряд запаса (0 — наружная колония,
# своя или занятая соседом; 1 — колония не узналась; 2 — российская; 3 — HTTPS не проходит),
# за ним потери и круг. По нему _st_warp_order ставит туннели в warp.up лучшим первым. Занятая
# колония в разряде не отличается от свободной: «занята соседом» важно, когда точку выбирают
# для одного туннеля, а сравнивать туннели между собой — это уже только связь.
_st_warp_scan() { # ИНТЕРФЕЙС КЛЮЧ_УЗЛА ЗАНЯТЫЕ_КОЛОНИИ ЗАНЯТЫЕ_АДРЕСА
local dev="$1" peer="$2" busy=" $3 " busyip=" $4 " r="$ST_RUN/scan.$1" cand ip ports port loss rtt colo pick f
local dev="$1" peer="$2" busy=" $3 " busyip=" $4 " r="$ST_RUN/scan.$1" cand ip ports port loss rtt colo pick f cls
mkdir -p "$ST_RUN"
: > "$r"; : > "$r.same"; : > "$r.nc"; : > "$r.ru"; : > "$r.notls"
# Якоря .1 первыми: на них указывает сам engage.cloudflareclient.com; случайные — за
@@ -5295,7 +5288,9 @@ _st_warp_scan() { # ИНТЕРФЕЙС КЛЮЧ_УЗЛА ЗАНЯТЫЕ_КОЛО
# не понимает модификаторов у ключей, см. brb/warp.sh).
for f in "$r" "$r.same" "$r.nc" "$r.ru" "$r.notls"; do
[ -s "$f" ] || continue
pick=$(awk '{ printf "%d\t%s %s %s\n", $1 * 100000 + $2, $3, $4, $5 }' "$f" | sort -n | head -n1 | cut -f2)
case "$f" in *.nc) cls=1 ;; *.ru) cls=2 ;; *.notls) cls=3 ;; *) cls=0 ;; esac
pick=$(awk -v c="$cls" '{ k = c * 100000000 + $1 * 100000 + $2; printf "%d\t%s %s %s %d\n", k, $3, $4, $5, k }' "$f" |
sort -n | head -n1 | cut -f2)
case "$f" in
*.same) echo " другой колонии нет — та же, но через другой адрес" >&2 ;;
*.ru) echo " наружных колоний нет — беру российскую: блокировки через неё не снимаются" >&2 ;;
@@ -5314,13 +5309,56 @@ _st_warp_alive_if() { # ИНТЕРФЕЙС — рукопожатие не ст
}
_st_warp_ready_if() { ifstatus "$1" 2>/dev/null | grep -q '"up": true' && awg show "$1" peers 2>/dev/null | grep -q .; }
# Порядок туннелей в warp.up — лучший первым. Порядок здесь не для красоты: его читают все,
# кому из нескольких туннелей нужен один или кто перебирает их по очереди.
# - _st_warp_first: через первый живой пробуются сервисы, качаются списки, идёт самопроверка.
# - Мост Telegram (warp.lst, см. _st_tgws_warp): берёт первый живой туннель из списка и к
# следующему переходит, только когда тот не дал даже TCP, — порядок для него и есть выбор.
# - Движок (devices в spec, prefer: latency): туннели он меряет сам, но замеры в пределах
# допуска (50 мс) считает равными и из равных берёт стоящий выше, — порядок решает ничьи, а
# когда замерить не вышло ни одного, выбор идёт просто по порядку.
#
# Ключ — от разведки (см. _st_warp_scan). У туннеля, оставленного живым, свежего замера нет, и
# дальше два случая.
# - Разведки не было ни у одного — туннели остаются на прежних местах. Переставлять их по
# трём пингам значило бы на каждом «Применить» и каждом прогоне автообхода тасовать список
# по дрожанию задержки, а новый порядок — это другой spec и перезапуск движка с обрывом
# соединений ради ничего.
# - Хоть один разведан заново — список всё равно меняется, и оставленным меряется то же, что
# мерила разведка (потери и круг тремя пингами изнутри живого туннеля, сессию это не
# трогает), с разрядом запаса 0: колония у них узналась и она не российская, иначе туннель
# ушёл бы в разведку. Без замера новичка не с чем было бы сравнить.
# При равных ключах держится порядок строк, то есть номеров туннелей.
_st_warp_order() { # ФАЙЛ «интерфейс колония ключ|-» -> тот же файл «интерфейс колония»
local f="$1" i c k pos=0 fresh=0
grep -q ' [0-9][0-9]*$' "$f" && fresh=1
: > "$f.key"
while read -r i c k; do
[ -n "$i" ] || continue
pos=$((pos + 1))
if [ "$fresh" = 0 ]; then
# Прежнее место; туннеля, которого в прошлом списке не было, — в хвост по номеру.
k=$(awk -v i="$i" '$1 == i { print NR; exit }' "$ST_WARP_UP" 2>/dev/null)
[ -n "$k" ] || k=$((100 + pos))
elif [ "$k" = - ]; then
set -- $(_st_warp_probe "$i")
k=$(($1 * 100000 + $2))
fi
# Ключ и номер строки — нулями до одной ширины, чтобы простой sort сравнил их как числа
# (busybox sort не понимает модификаторов у ключей, см. brb/warp.sh).
printf '%012d %03d %s %s\n' "$k" "$pos" "$i" "$c" >> "$f.key"
done < "$f"
sort "$f.key" | awk '{ print $3, $4 }' > "$f"
rm -f "$f.key"
}
# Поднять туннели. repick — не держаться прежних точек, подобрать заново.
#
# Сначала ключи и интерфейсы ВСЕХ туннелей одним reload, и только потом разведка на живых
# интерфейсах через `awg set`: запись в uci с ifup посреди разведки делала `network reload`, и
# netifd перезапускал соседние туннели (на стенде так пропадал уже подобранный первый).
_st_warp_up() { # [repick]
local repick="$1" n i peer got busy="" busyip="" ru=0 c ready="" w need=0 colos=""
local repick="$1" n i peer got busy="" busyip="" ru=0 c ready="" w need=0 colos="" alt p
_st_install_awg || return 1
# Какие порты провайдер выпускает, меняется: при подборе заново спрашиваем снова.
[ "$repick" = repick ] && rm -f "$ST_DIR/warp.ports"
@@ -5365,7 +5403,9 @@ _st_warp_up() { # [repick]
# держаться за неё только потому, что туннель жив, — значит оставить геоблок навсегда
# (на тестовом роутере так и вышло: HEL, DME, HEL). Такой туннель разводится заново.
if [ "$repick" != repick ] && _st_warp_alive_if "$i" && c="$(_st_colo_of "$i")" && ! _st_warp_is_ru "$c"; then
got="$(uci -q get "network.${i}_peer.endpoint_host") $(uci -q get "network.${i}_peer.endpoint_port") $c"
# Ключа качества нет («-»): свежего замера у оставленного туннеля нет, его место в
# warp.up решает _st_warp_order.
got="$(uci -q get "network.${i}_peer.endpoint_host") $(uci -q get "network.${i}_peer.endpoint_port") $c -"
_rb_say "WARP $n работает: колония $c"
else
echo " WARP $n: разведка"
@@ -5375,9 +5415,25 @@ _st_warp_up() { # [repick]
# endpoint` нового рукопожатия не начинает — нужен тот же сброс узла, что в
# разведке, иначе туннель полминуты молчит и идёт не в ту колонию.
if ! _st_warp_link "$i" "$peer" "$1" "$2"; then
# Запасной порт — второй найденный в warp.ports. Разведка ведёт все адреса
# ОДНИМ портом, первым выпущенным, и точка, ответившая на нём минуту назад,
# может замолчать именно на нём: провайдер режет UDP к WARP по портам, и не
# всегда с первой минуты. Адрес тот же, значит и колония та же — она за
# адресом, не за портом; ключ качества остаётся от разведки.
alt=""
for p in $(cat "$ST_DIR/warp.ports" 2>/dev/null); do
[ "$p" = "$2" ] && continue
_st_stopped && break
_st_warp_link "$i" "$peer" "$1" "$p" && { alt="$p"; break; }
done
if [ -z "$alt" ]; then
_rb_warn "WARP $n: выбранная точка $1:$2 не ответила повторно"
continue
fi
_rb_say "WARP $n: точка $1:$2 не ответила повторно, беру запасной порт $alt"
got="$1 $alt $3 $4"
set -- $got
fi
_rb_say "WARP $n работает: $1:$2, колония $3"
else
_rb_warn "WARP $n не поднялся"
@@ -5387,8 +5443,7 @@ _st_warp_up() { # [repick]
set -- $got
busy="$busy $3"; busyip="$busyip $1"
_st_warp_is_ru "$3" && ru=1
colos="${colos:+$colos, }$3"
echo "$i $3" >> "$ST_WARP_UP.tmp"
echo "$i $3 ${4:--}" >> "$ST_WARP_UP.tmp"
# В uci — для следующей загрузки. Без reload: живой интерфейс уже на этой точке.
uci set "network.${i}_peer.endpoint_host=$1"
uci set "network.${i}_peer.endpoint_port=$2"
@@ -5400,7 +5455,9 @@ _st_warp_up() { # [repick]
return 1
fi
uci commit network
_st_warp_order "$ST_WARP_UP.tmp"
mv "$ST_WARP_UP.tmp" "$ST_WARP_UP"
colos="$(awk '{ printf "%s%s", (NR > 1 ? ", " : ""), $2 }' "$ST_WARP_UP")"
# Не поднявшиеся — погасить: висящий без рукопожатия туннель движок принял бы за живой.
for n in $ready; do
i="$(_st_wif "$n")"