mirror of
https://github.com/karen07/openwrt-mesh-builder.git
synced 2026-10-19 07:23:32 +00:00
Replace Watchcat and normalize direct IPv4 lists
- Remove the Watchcat configuration and package dependency - Add generated ping-reboot watchdog settings with boot grace, retry interval, failure threshold, and ping timeout - Document the custom connectivity watchdog behavior and manual check mode - Accept both CIDR prefixes and individual IPv4 addresses from IPinfo Lite - Normalize individual IPv4 addresses to explicit /32 prefixes - Apply direct-list normalization consistently on routers and AWG servers - Document that generated direct lists always use explicit IPv4 CIDR notation
This commit is contained in:
parent
87bf78b12c
commit
941a61de35
9 changed files with 174 additions and 21 deletions
29
README.md
29
README.md
|
|
@ -815,7 +815,7 @@ Direct lists собираются из нескольких источников
|
|||
- публичные `listen_ip` и `exit_ip` из `config.json`
|
||||
- дополнительные CIDR prefixes из `EXIT_DIRECT_STATIC_IPSETS`
|
||||
|
||||
Динамическая часть задается странами и ASN. Страны берутся из IPinfo Lite через `Alice39s/ipinfo-csv-lite`: daily release содержит готовые CIDR, country code, continent и ASN. Для `DIRECT_COUNTRIES` выбираются только нужные IPv4 CIDR. ASN lists по-прежнему берутся из `ipverse/as-ip-blocks`.
|
||||
Динамическая часть задается странами и ASN. Страны берутся из IPinfo Lite через `Alice39s/ipinfo-csv-lite`: daily release содержит готовые CIDR, country code, continent и ASN. Для `DIRECT_COUNTRIES` выбираются нужные IPv4 prefixes; одиночные IPv4 записи источника нормализуются в `/32`. ASN lists по-прежнему берутся из `ipverse/as-ip-blocks`. Итоговый `direct.txt` всегда хранит IPv4 в явной CIDR-нотации.
|
||||
|
||||
На роутерах и exit серверах эти настройки лежат в runtime env:
|
||||
|
||||
|
|
@ -824,7 +824,7 @@ DIRECT_COUNTRIES='ru cn by'
|
|||
DIRECT_ASNS='32590'
|
||||
```
|
||||
|
||||
`update-ipsets.sh` читает `/etc/ipsets/direct-static.txt`, скачивает latest `ipinfo-lite.csv.gz` во временный файл, проверяет gzip и через `grep` + `cut` выбирает готовые IPv4 CIDR для всех `DIRECT_COUNTRIES`, затем добавляет ASN lists. Распакованный CSV в `/tmp` не сохраняется, а gzip удаляется сразу после фильтрации. После этого скрипт атомарно обновляет `/etc/ipsets/direct.txt` и перезагружает firewall только если итоговый список изменился. Python или IPinfo libraries на OpenWrt не нужны.
|
||||
`update-ipsets.sh` читает `/etc/ipsets/direct-static.txt`, скачивает latest `ipinfo-lite.csv.gz` во временный файл, проверяет gzip и через `grep` + `cut` выбирает IPv4 prefixes для всех `DIRECT_COUNTRIES` (включая одиночные адреса без `/32`), затем добавляет ASN lists. Перед записью `direct.txt` все одиночные IPv4 адреса нормализуются в явный `/32`. Распакованный CSV в `/tmp` не сохраняется, а gzip удаляется сразу после фильтрации. После этого скрипт атомарно обновляет `/etc/ipsets/direct.txt` и перезагружает firewall только если итоговый список изменился. Python или IPinfo libraries на OpenWrt не нужны.
|
||||
|
||||
Тот же `update-ipsets.sh` запускается один раз локально во время `generate.py` с подмененным `IPSETS_DIR` и `RELOAD_FIREWALL=0`. Поэтому initial `direct-static.txt` и `direct.txt`, которые копируются во все router/server trees, строятся той же runtime реализацией, которая затем работает на OpenWrt.
|
||||
|
||||
|
|
@ -961,7 +961,6 @@ etc/awg-server.env
|
|||
- init scripts
|
||||
- cron
|
||||
- DoH
|
||||
- watchcat
|
||||
- network/firewall tails
|
||||
- bootstrap
|
||||
|
||||
|
|
@ -1045,6 +1044,29 @@ Bootstrap скрипт на OpenWrt при первом запуске обра
|
|||
- для включенных Wi-Fi radio выбирает максимальную поддерживаемую ширину и самый низкий доступный канал
|
||||
- делает `uci commit`
|
||||
|
||||
## Connectivity watchdog
|
||||
|
||||
Вместо `watchcat` проект использует простой `/etc/scripts/ping-reboot.sh` с procd-службой `/etc/init.d/ping-reboot`.
|
||||
|
||||
После загрузки роутера watchdog дает системе до 5 минут (`300` секунд uptime) на поднятие сети. Если службу перезапустить позже вручную, новая 5-минутная пауза не начинается: grace считается именно от uptime роутера.
|
||||
|
||||
Параметры watchdog генерируются в `/etc/router-autoinstall.env`: адреса для ping, startup grace, интервал проверки, число неудач и timeout ping.
|
||||
|
||||
После startup grace скрипт раз в 10 секунд проверяет:
|
||||
|
||||
```text
|
||||
195.208.4.1
|
||||
77.88.8.8
|
||||
```
|
||||
|
||||
Достаточно ответа хотя бы от одного адреса. Если три последовательных 10-секундных периода заканчиваются без ответа от обоих адресов, роутер перезагружается. Успешный ping сбрасывает счетчик ошибок.
|
||||
|
||||
Для ручной проверки без reboot loop:
|
||||
|
||||
```sh
|
||||
/etc/scripts/ping-reboot.sh once
|
||||
```
|
||||
|
||||
## DoH и DNS failover
|
||||
|
||||
Для каждого роутера генератор ведет managed DNS-записи в начале
|
||||
|
|
@ -1419,7 +1441,6 @@ iperf3
|
|||
jq-full
|
||||
luci
|
||||
luci-app-https-dns-proxy
|
||||
luci-app-watchcat
|
||||
luci-proto-amneziawg
|
||||
luci-proto-ipip
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
config watchcat
|
||||
option mode 'ping_reboot'
|
||||
option period '30s'
|
||||
option pinghosts '195.208.4.1 77.88.8.8'
|
||||
option addressfamily 'ipv4'
|
||||
option pingperiod '10s'
|
||||
option pingsize 'standard'
|
||||
option forcedelay '10s'
|
||||
16
routers/example/files/etc/init.d/ping-reboot
Executable file
16
routers/example/files/etc/init.d/ping-reboot
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
USE_PROCD=1
|
||||
START=99
|
||||
|
||||
prog_name="ping-reboot.sh"
|
||||
bin="/etc/scripts/$prog_name"
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$bin"
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
109
routers/example/files/etc/scripts/ping-reboot.sh
Executable file
109
routers/example/files/etc/scripts/ping-reboot.sh
Executable file
|
|
@ -0,0 +1,109 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ -r /etc/router-autoinstall.env ]; then
|
||||
# Generated file, loaded once at startup. Restart to apply changes.
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/router-autoinstall.env
|
||||
fi
|
||||
|
||||
HOSTS="${PING_REBOOT_HOSTS:-}"
|
||||
BOOT_GRACE="${PING_REBOOT_BOOT_GRACE:-300}"
|
||||
INTERVAL="${PING_REBOOT_INTERVAL:-10}"
|
||||
MAX_FAILURES="${PING_REBOOT_MAX_FAILURES:-3}"
|
||||
TIMEOUT="${PING_REBOOT_TIMEOUT:-2}"
|
||||
|
||||
if [ -z "$HOSTS" ]; then
|
||||
echo "PING_REBOOT_HOSTS is empty in /etc/router-autoinstall.env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
router_uptime_seconds() {
|
||||
awk '{ print int($1) }' /proc/uptime 2>/dev/null
|
||||
}
|
||||
|
||||
wait_for_boot_grace() {
|
||||
uptime_s="$(router_uptime_seconds)"
|
||||
|
||||
case "$uptime_s" in
|
||||
'' | *[!0-9]*)
|
||||
echo "cannot read router uptime; waiting full ${BOOT_GRACE}s startup grace"
|
||||
sleep "$BOOT_GRACE"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$uptime_s" -lt "$BOOT_GRACE" ]; then
|
||||
remaining=$((BOOT_GRACE - uptime_s))
|
||||
echo "startup grace: router uptime=${uptime_s}s, waiting ${remaining}s"
|
||||
sleep "$remaining"
|
||||
fi
|
||||
}
|
||||
|
||||
any_host_reachable() {
|
||||
host=""
|
||||
|
||||
for host in $HOSTS; do
|
||||
if ping -c 1 -W "$TIMEOUT" "$host" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
apply_once() {
|
||||
if any_host_reachable; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
case "${1:-run}" in
|
||||
once)
|
||||
if apply_once; then
|
||||
echo "connectivity OK"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "all ping hosts unreachable"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
run)
|
||||
wait_for_boot_grace
|
||||
failures=0
|
||||
|
||||
echo "started: hosts=[$HOSTS] interval=${INTERVAL}s"
|
||||
echo "max_failures=$MAX_FAILURES timeout=${TIMEOUT}s"
|
||||
|
||||
while true; do
|
||||
sleep "$INTERVAL"
|
||||
|
||||
if apply_once; then
|
||||
if [ "$failures" -gt 0 ]; then
|
||||
echo "connectivity restored after ${failures} failed check(s)"
|
||||
fi
|
||||
failures=0
|
||||
continue
|
||||
fi
|
||||
|
||||
failures=$((failures + 1))
|
||||
echo "all ping hosts unreachable: ${failures}/${MAX_FAILURES}"
|
||||
|
||||
if [ "$failures" -ge "$MAX_FAILURES" ]; then
|
||||
echo "connectivity lost; rebooting router"
|
||||
sync
|
||||
reboot
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 [run|once]" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
|
@ -115,7 +115,7 @@ append_country_lists() {
|
|||
fi
|
||||
|
||||
gzip -dc "$TMP_IPINFO" \
|
||||
| grep -E "^[0-9.]+/[0-9]+,(${country_regex})," \
|
||||
| grep -E "^[0-9.]+(/[0-9]+)?,(${country_regex})," \
|
||||
| cut -d, -f1 >"$TMP_COUNTRIES"
|
||||
|
||||
if [ ! -s "$TMP_COUNTRIES" ]; then
|
||||
|
|
@ -183,9 +183,10 @@ if [ ! -s "$TMP_DIRECT" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
LC_ALL=C sort -u "$TMP_DIRECT" >"$TMP_SORTED" || {
|
||||
sed '/\//! s#$#/32#' "$TMP_DIRECT" \
|
||||
| LC_ALL=C sort -u >"$TMP_SORTED" || {
|
||||
rm -f "$TMP_DIRECT" "$TMP_SORTED" "$TMP_IPINFO" "$TMP_COUNTRIES"
|
||||
logger -t "$TAG" "ERROR: failed to sort direct list"
|
||||
logger -t "$TAG" "ERROR: failed to normalize/sort direct list"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ append_country_lists() {
|
|||
fi
|
||||
|
||||
gzip -dc "$TMP_IPINFO" \
|
||||
| grep -E "^[0-9.]+/[0-9]+,(${country_regex})," \
|
||||
| grep -E "^[0-9.]+(/[0-9]+)?,(${country_regex})," \
|
||||
| cut -d, -f1 >"$TMP_COUNTRIES"
|
||||
|
||||
if [ ! -s "$TMP_COUNTRIES" ]; then
|
||||
|
|
@ -459,9 +459,10 @@ build_direct_list() {
|
|||
die "generated direct list is empty"
|
||||
fi
|
||||
|
||||
LC_ALL=C sort -u "$TMP_DIRECT" >"$TMP_SORTED" || {
|
||||
"$SED_BIN" '/\//! s#$#/32#' "$TMP_DIRECT" \
|
||||
| LC_ALL=C sort -u >"$TMP_SORTED" || {
|
||||
rm -f "$TMP_DIRECT" "$TMP_SORTED" "$TMP_IPINFO" "$TMP_COUNTRIES"
|
||||
die "failed to sort $TMP_DIRECT"
|
||||
die "failed to normalize/sort $TMP_DIRECT"
|
||||
}
|
||||
|
||||
mv -f "$TMP_SORTED" "$TMP_DIRECT" || {
|
||||
|
|
|
|||
|
|
@ -163,7 +163,6 @@ SYNC_COPY_DIRS = {
|
|||
|
||||
SYNC_COPY_FILES = {
|
||||
Path("files/etc/config/https-dns-proxy"),
|
||||
Path("files/etc/config/watchcat"),
|
||||
}
|
||||
|
||||
SYNC_MERGE_FILES = {
|
||||
|
|
@ -355,7 +354,6 @@ ROUTER_REQUIRED_PACKAGES = [
|
|||
"iperf3",
|
||||
"jq-full",
|
||||
"luci-app-https-dns-proxy",
|
||||
"luci-app-watchcat",
|
||||
"luci-proto-amneziawg",
|
||||
"luci-proto-ipip",
|
||||
"luci",
|
||||
|
|
@ -420,6 +418,12 @@ CHECK_DOH_RESOLV = "/tmp/resolv.conf.d/resolv.conf.auto"
|
|||
CHECK_DOH_RESOLV_WAIT_MAX = 300
|
||||
CHECK_DOH_PROVIDER_DOMAINS = ["ru", "xn--p1ai"]
|
||||
|
||||
PING_REBOOT_HOSTS = ["195.208.4.1", "77.88.8.8"]
|
||||
PING_REBOOT_BOOT_GRACE = 300
|
||||
PING_REBOOT_INTERVAL = 10
|
||||
PING_REBOOT_MAX_FAILURES = 5
|
||||
PING_REBOOT_TIMEOUT = 2
|
||||
|
||||
ROUTING_MODE_WAN = "wan"
|
||||
ROUTING_MODE_SPLIT = "split"
|
||||
ROUTING_MODE_EXIT = "exit"
|
||||
|
|
|
|||
|
|
@ -141,6 +141,11 @@ def build_runtime_env(cfg: ConfigData, router_name: str | None = None) -> str:
|
|||
"CHECK_DOH_RESOLV": CHECK_DOH_RESOLV,
|
||||
"CHECK_DOH_RESOLV_WAIT_MAX": str(CHECK_DOH_RESOLV_WAIT_MAX),
|
||||
"CHECK_DOH_PROVIDER_DOMAINS": " ".join(CHECK_DOH_PROVIDER_DOMAINS),
|
||||
"PING_REBOOT_HOSTS": " ".join(PING_REBOOT_HOSTS),
|
||||
"PING_REBOOT_BOOT_GRACE": str(PING_REBOOT_BOOT_GRACE),
|
||||
"PING_REBOOT_INTERVAL": str(PING_REBOOT_INTERVAL),
|
||||
"PING_REBOOT_MAX_FAILURES": str(PING_REBOOT_MAX_FAILURES),
|
||||
"PING_REBOOT_TIMEOUT": str(PING_REBOOT_TIMEOUT),
|
||||
"EXIT_ROUTE_TABLE": str(EXIT_POLICY_BASE),
|
||||
"EXIT_ROUTE_INTERVAL": str(EXIT_ROUTE_INTERVAL),
|
||||
"EXIT_ROUTE_TARGETS": " ".join(target_names),
|
||||
|
|
|
|||
|
|
@ -373,6 +373,11 @@ def expected_runtime_env_values(
|
|||
"CHECK_DOH_RESOLV": CHECK_DOH_RESOLV,
|
||||
"CHECK_DOH_RESOLV_WAIT_MAX": str(CHECK_DOH_RESOLV_WAIT_MAX),
|
||||
"CHECK_DOH_PROVIDER_DOMAINS": " ".join(CHECK_DOH_PROVIDER_DOMAINS),
|
||||
"PING_REBOOT_HOSTS": " ".join(PING_REBOOT_HOSTS),
|
||||
"PING_REBOOT_BOOT_GRACE": str(PING_REBOOT_BOOT_GRACE),
|
||||
"PING_REBOOT_INTERVAL": str(PING_REBOOT_INTERVAL),
|
||||
"PING_REBOOT_MAX_FAILURES": str(PING_REBOOT_MAX_FAILURES),
|
||||
"PING_REBOOT_TIMEOUT": str(PING_REBOOT_TIMEOUT),
|
||||
"EXIT_ROUTE_TABLE": str(EXIT_POLICY_BASE),
|
||||
"EXIT_ROUTE_INTERVAL": str(EXIT_ROUTE_INTERVAL),
|
||||
"EXIT_ROUTE_TARGETS": " ".join(target_names),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue