0xkee Posted July 6 Author Posted July 6 (edited) 5 hours ago, Valerios said: Добрый день месяц уже Вашим творением пользуюсь, работает шикарно, спасибо огромное Вам, самое удобное и простое решение по моему. Добрый Спасибо! 5 hours ago, Valerios said: по поводу рекламы в ют на iOS с приложением shadowrocket у меня получилось реализовать выключение рекламы перед видео, так же работает окно в окне и фоновое воспроизведение инструкцию случайно нашел у одного блогера нельзя ли как-то подобным образом это на роутере сделать? Какие данные мне предоставить, которые бы дали понимание ? в кратце попробую способ описать: в приложении в настройке конфига подключения включается https, сертификат загружается в телефон и включается доверие, потом в конфиге загружается 2 модуля, модуль сертификата и модуль Модуль ют Спасибо, предоставленной информации вполне достаточно. К сожалению... Интернет, он как слоёный пирог, или селёдка по шубой, или салат мимоза. Там несколько технологических уровней пирога, примерно 7 (специалисты спорят, сколько именно). Любой роутер (Кинетик тоже) - это достаточно слабое устройство, примерно в 10-100 раз слабее любого смартфона или ПК. Он работает, так как находится на 2-3 уровне, где не требуется много вычислений или памяти. Это специальное устройство для ограниченного круга задач. Фильтрация YT - это задача уровня 6-7, самый верх пирога. Это требует в разы больше ресурсов устройства. Теоретически это можно (попробовать) сделать, но практически это не будет работать (это точно). Нельзя перенести этот подход на роутер. Причина фундаментальная — это не фильтрация на уровне DNS/IP, а MITM с модификацией бинарных API-ответов внутри HTTPS-сессии. Роутер Keenetic не потянет ни по CPU, ни по RAM, и всё равно придётся ставить сертификат на каждое устройство (что ничем не лучше Shadowrocket на каждом устройстве отдельно). Для пользователя оптимальная стратегия: Shadowrocket на iOS + ReVanced на Android + uBlock в браузере ПК. Каждая платформа — своё решение. Технические подробности: Spoiler # YouTube Premium через роутер (как Shadowrocket) — анализ > **Дата:** 2026-07-06 > **Источник:** запрос пользователя Valerios на форуме (NC-1812) > **Статус:** ❌ Нецелесообразно ## Запрос Пользователь нашёл способ блокировки рекламы YouTube на iOS через Shadowrocket: - HTTPS-перехват с установкой сертификата - Модуль блокировки рекламы + PiP + фоновое воспроизведение - Вопрос: можно ли реализовать аналогичное на роутере Keenetic? **Ссылки от пользователя:** - https://apps.apple.com/ru/app/shadowrocket/id932747118 - https://raw.githubusercontent.com/misha-tgshv/shadowrocket-configuration-file/refs/heads/main/modules/YT-Premium-V1-RU.module ## Как работает Shadowrocket-модуль Изучены оба файла — `.module` и `youtube.response.js` (~2500 строк минифицированного кода). Механизм состоит из 3 уровней: ### 1. MITM (Man-in-the-Middle) — ключевой элемент ``` [MITM] hostname = *.googlevideo.com, www.youtube.com, s.youtube.com, youtubei.googleapis.com ``` Shadowrocket **расшифровывает HTTPS-трафик** к этим доменам. Для этого генерирует собственный CA-сертификат, устанавливает его в доверенные на устройстве, и подменяет SSL-сертификаты налету. ### 2. URL Rewrite — блокировка рекламных запросов - `googlevideo.com/initplayback...&oad` → reject (рекламные pre-roll) - `youtube.com/api/stats/ads` → reject - `youtube.com/pagead`, `ptracking` → reject - UDP-трафик к `googlevideo.com` и `youtubei.googleapis.com` → reject (QUIC) ### 3. Script — модификация protobuf-ответов API JS-скрипт перехватывает ответы `youtubei.googleapis.com/youtubei/v1/{browse,next,player,search,...}` и: - **Парсит бинарный Protocol Buffers** (не JSON!) ответ YouTube - **Удаляет** `adPlacements`, `adSlots`, `pageadViewthroughconversion` - **Инжектит** `pictureInPictureRender` с `active: true` → PiP - **Инжектит** `backgroundPlayerRender` с `active: true` → фоновое воспроизведение - **Удаляет** Shorts из навигации - **Пересобирает** protobuf и отдаёт модифицированный ответ ## Можно ли сделать это на роутере Keenetic? **Короткий ответ: технически возможно, но практически — нет. Не имеет смысла.** ### Причины | Проблема | Детали | |----------|--------| | **MITM HTTPS** | Нужен SSL-прокси (mitmproxy / squid ssl-bump), который расшифровывает HTTPS на лету. Это +100-300% CPU на каждое соединение | | **Ресурсы NC-1812** | ~880 МГц MIPS/ARM, 256 МБ RAM. SSL interception одного YouTube-потока = 30-50% CPU. Несколько устройств = роутер ляжет | | **Protobuf-обработка** | Нужен JavaScript-рантайм (Node.js) на роутере для парсинга и модификации бинарных protobuf-ответов YouTube API. Node.js на MIPS = ~80 МБ RAM | | **Сертификат на каждом устройстве** | Всё равно придётся ставить CA-сертификат роутера на КАЖДОЕ устройство в сети — iPhone, Android, ПК, TV | | **Certificate Pinning** | Приложение YouTube на Android/iOS использует certificate pinning — отклонит поддельный сертификат. Работать будет только в браузере | | **QUIC/HTTP3** | YouTube активно использует QUIC (UDP). На роутере надо блокировать весь QUIC чтобы заставить YouTube использовать TCP/HTTPS | ### Почему DNS-блокировка (AdGuard Home / Pi-hole) не поможет - Реклама YouTube идёт с **тех же самых доменов** что и контент (`googlevideo.com`) - На уровне DNS невозможно отличить рекламный видеопоток от контентного - PiP и фоновое воспроизведение — это модификация API-ответов, DNS тут бессилен ## Что реально работает (альтернативы) | Платформа | Решение | Как работает | |-----------|---------|--------------| | **iOS** | Shadowrocket (что у пользователя) | MITM-прокси на устройстве | | **Android** | ReVanced | Патченый YouTube-клиент, не требует прокси | | **ПК (браузер)** | uBlock Origin | Блокирует рекламу в веб-версии YouTube | | **SmartTV / AppleTV** | YouTube Premium | Для них решений на уровне роутера нет вообще | Видиться 3 варианта: 1. текущий - каждое устройство - своё решение 2. купить yt premium (Shadowrocket - 4.5$, yt - 9$/month, разница заметна, но Серёга Брин и Ларри Пейдж вроде заслужили), + там ещё есть фишки, которых нет во free 3. Найти и прислать аналогичное решение именно для роутера (не на базе ПК, а как отдельного устройства), тогда от него можно попробовать оттолкнуться. На данном этапе нормальных решений не найдено 🙁 🤷♂️ Edited July 6 by 0xkee 1 Quote
nikolay1980 Posted July 9 Posted July 9 (edited) Здравствуйте. Спасибо за приложение, очень удобно всё сделано. Единственное не могу настроить smart dns - идет только через системный. И тесты в smartdns geo-config не проходят: ✗ error · script_error. И поумолчанию и с моими настройками никак не работает.вместо системного DoH adguard ставил и от провайдера и просто 1.1.1.1 - всеравно не работает. bag-report.txt Edited July 10 by nikolay1980 Quote
0xkee Posted July 11 Author Posted July 11 On 7/9/2026 at 7:32 PM, nikolay1980 said: Здравствуйте. Спасибо за приложение, очень удобно всё сделано. Единственное не могу настроить smart dns - идет только через системный. И тесты в smartdns geo-config не проходят: ✗ error · script_error. И поумолчанию и с моими настройками никак не работает.вместо системного DoH adguard ставил и от провайдера и просто 1.1.1.1 - всеравно не работает. Добрый день. 1. error - нужно обновить все пакеты После этого dns-check.sh появится (версия smartdns-geo-conf 0.10.6+), и DNS Check заработает. Но лучше подождите 0.10.7+, день-два, там улучшения в части dns check web ui. 2. ("идет только через системный") — в баг-репорте всё работает корректно (split active, DNS тесты проходят, маршрутизация split), так что скорее всего это тоже решится обновлением... 1 Quote
nikolay1980 Posted July 11 Posted July 11 Спасибо. Решил прописав в системный dns кинетика 192.168.1.1:6053, теперь работает. И после обновления smartdns-geo-conf 0.10.6+ DNS Check заработал. Quote
0xkee Posted July 11 Author Posted July 11 23 minutes ago, nikolay1980 said: Решил прописав в системный dns кинетика 192.168.1.1:6053 Это должен делать smartdns-redirect, это его единственное назначение, но можно и так 24 minutes ago, nikolay1980 said: И после обновления smartdns-geo-conf 0.10.6+ DNS Check заработал. 👍 вот новые версии, там сильно лучше с визуализацией checks geo-split-data_0.6.0_all.ipk geo-split_0.17.0_all.ipk keenetic-entware-extras_0.16.6_all.ipk smartdns-geo-conf_0.10.7_all.ipk smartdns-redirect_0.3.4_all.ipk webui_0.32.2_all.ipk Quote
nikolay1980 Posted July 11 Posted July 11 Спасибо за отличную работу.Обнаружил проблему с Private AdGuard DoH (https://d.adguard-dns.com/dns-query/<ID>). При генерации server-https с параметром -host-ip запросы идут, но не попадают в персональный профиль AdGuard DNS. После удаления -host-ip всё начинает работать корректно и запросы отображаются в личном кабинете. Возможно, для пользовательских DoH-провайдеров не стоит принудительно задавать -host-ip. Quote
0xkee Posted July 11 Author Posted July 11 2 hours ago, nikolay1980 said: Обнаружил проблему с Private AdGuard DoH (https://d.adguard-dns.com/dns-query/<ID>). При генерации server-https с параметром -host-ip запросы идут, но не попадают в персональный профиль AdGuard DNS. После удаления -host-ip всё начинает работать корректно и запросы отображаются в личном кабинете. Возможно, для пользовательских DoH-провайдеров не стоит принудительно задавать -host-ip. Поправлено. Теперь для DOH серверов можно не указывать IP1/2, а для private DOH - не нужно. Тогда -host-ip не будет в итоговом конфиге. Отражено в документации. + слегка улучшены ui чекеры Пожалуйста, подтвердите... geo-split-data_0.6.0_all.ipk geo-split_0.17.3_all.ipk keenetic-entware-extras_0.16.7_all.ipk smartdns-geo-conf_0.10.8_all.ipk smartdns-redirect_0.3.4_all.ipk webui_0.32.5_all.ipk Quote
nikolay1980 Posted July 11 Posted July 11 В этом скрипте надо поправить /opt/keenetic-entware-extras/smartdns-geo-conf/scripts/generate-conf.sh секцию _emit_doh_server, тогда в /opt/etc/smartdns/dns-servers-other.conf они не прописываются: server-https https://d.adguard-dns.com/dns-query/123abcd \ -host-name d.adguard-dns.com \ -http-host d.adguard-dns.com \ -tls-host-verify d.adguard-dns.com \ -interface nwg0 Сам пытался поправить, но что то не получается. Quote
0xkee Posted July 11 Author Posted July 11 6 minutes ago, nikolay1980 said: В этом скрипте надо поправить /opt/keenetic-entware-extras/smartdns-geo-conf/scripts/generate-conf.sh секцию _emit_doh_server, тогда в /opt/etc/smartdns/dns-servers-other.conf они не прописываются: поправлено.... в версии smartdns-geo-conf_0.10.8_all.ipk Spoiler Quote
dimsok Posted July 12 Posted July 12 Доброе утро! Еще раз спасибо! Подскажите а почему intel.com определяет меня как Россия? Quote
0xkee Posted July 12 Author Posted July 12 2 hours ago, dimsok said: Доброе утро! Еще раз спасибо! Подскажите а почему intel.com определяет меня как Россия? Доброе утро. Пришлите, пожалуйста, скрин и url ! Вроде не определяет как Россия, но может кудато не туда смотрю? Можно в личку сообщением. Quote
KeenTaur Posted July 13 Posted July 13 (edited) В 06.07.2026 в 22:46, 0xkee сказал: пишите какие вопросы остались! Ну, напрямую к geoip оставшиеся вопросы отношения не имеют. Но если сможете подсказать, буду рад: какой хук реагирует на обновление/продление ip-адреса? или как еще поправить ситуацию, когда при обновлении ip-адреса обновляется и вся информация, полученная от dhcp-сервера, в том числе и маршрут по умолчанию перезаписывается у меня с awg0 обратно на интерфейс ISP. Скрипт в /opt/etc/ndm/wan.d/ вероятно не срабатывает при обновлении/продлении аренды. Может быть /opt/etc/ndm/ifipchanged.d? Правда, в описании сказано, что сработает при изменении ip-адреса, но адрес-то остается тот же, продлевается его аренда. Цитата вебка есть отдельная от прошивки (:8080/custom/), без интеграции, но она использует стоковый rest api, а он 99% что менялся... У меня эксперименты с неподдерживаемой уже ОС 2.16 Спойлер Да и пусть бы без интеграции. Если интересно, происходит следующее: ~ # opkg install /opt/root/Distr/GeoIP/webui_0.33.2_all.ipk Installing webui (0.33.2) to root... Configuring webui. logrotate config deployed: /opt/etc/logrotate.d/nginx-webui Stopping nginx WebUI dashboard... not running (no PID). Starting nginx WebUI dashboard... ERROR: main-*.js not found in /tmp/ew-webui webui installed. Dashboard: http://10.5.33.1:8080/custom/ Config: /opt/keenetic-entware-extras/webui/config/nginx.conf Status: /opt/keenetic-entware-extras/webui/scripts/status.sh Нет nginx, нет и странички. Может я опять что-то простое упустил? С intel.com у меня ситуация как и у dimsok. Скриншот с сайтом и трассировкой через туннель и url. В конфигурации geoip сейчас только гео-зона ru. Спойлер url: https://www.intel.com без www, просто intel.com не открывается. Edited July 13 by KeenTaur Quote
RWH Posted July 13 Posted July 13 @KeenTaur, у меня нет такой проблемы, нормально открывается intel.com Спойлер 1 Quote
0xkee Posted July 13 Author Posted July 13 1 hour ago, KeenTaur said: Ну, напрямую к geoip оставшиеся вопросы отношения не имеют. Но если сможете подсказать, буду рад: какой хук реагирует на обновление/продление ip-адреса? Почему не подсказать... Иишка считает так: Spoiler Ответ для KeenTaur: хуки NDM и продление DHCP-аренды Короткий ответ Ни один стандартный NDM хук не гарантированно срабатывает при продлении DHCP-аренды без смены IP. Это фундаментальная проблема подхода "подменить default route в main table". Разбор хуков Директория Когда срабатывает Продление аренды (тот же IP) /opt/etc/ndm/wan.d/ WAN up/down события ❌ Не срабатывает — WAN не падал /opt/etc/ndm/ifstatechanged.d/ Смена состояния интерфейса (up/down/link) ⚠️ На некоторых прошивках NDM может кратковременно "передёрнуть" интерфейс при ребинде — тогда сработает. Не гарантировано. /opt/etc/ndm/ifipchanged.d/ Смена IP-адреса интерфейса ❌ Не срабатывает — IP не менялся /opt/etc/ndm/netfilter.d/ NDM перестраивает iptables ❌ К маршрутам не относится Почему это происходит При DHCP renew/rebind клиент получает от сервера полный набор опций, включая option 3 (Router). Keenetic NDM обрабатывает ответ и восстанавливает default route через ISP-интерфейс — это штатное поведение, NDM считает, что он "владеет" маршрутом по умолчанию. Решения (от лучшего к худшему) 1. Приоритеты подключений через Keenetic UI (рекомендуемый) Если нужно весь трафик пустить через awg0 — настройте приоритет подключений в веб-интерфейсе Keenetic: Интернет → Приоритеты подключений (или Маршруты и правила на новых прошивках) Поставьте awg0 выше ISP-подключения Тогда NDM сам управляет default route и при DHCP renew корректно восстановит его через awg0, а не через ISP. Это единственный путь, при котором NDM не будет бороться с вами за default route. 2. Custom routing table + ip rule (надёжный обходной путь) Не трогайте default route в main table вообще. Вместо этого: # Создать свою таблицу с default через awg0 ip route add default dev awg0 table 100 # Подключить её с высоким приоритетом (ниже номер = выше приоритет) ip rule add from all lookup 100 priority 100 DHCP renew перезаписывает default route только в main table (priority 32766). Ваша таблица 100 с priority 100 проверяется раньше — трафик уходит в awg0. Именно этот подход используется в geo-split (таблицы 1000/1001, priority 50/51) — DHCP не затрагивает кастомные таблицы. Скрипт для /opt/etc/ndm/ifstatechanged.d/ (страховка на случай поднятия awg0): #!/opt/bin/sh [ "${1:-}" = "hook" ] || exit 0 # Проверить что правило на месте ip rule show | grep -q "lookup 100" || { ip route replace default dev awg0 table 100 ip rule add from all lookup 100 priority 100 } 3. Cron watchdog (грубый но рабочий fallback) # /opt/etc/crontab — проверка каждую минуту * * * * * ip route | grep "^default" | grep -q "dev awg0" || ip route replace default dev awg0 Задержка до 1 минуты, но 100% надёжность. Рекомендация Если цель — весь трафик через VPN: используйте вариант 1 (приоритеты через UI Keenetic). NDM будет союзником, а не противником. Если цель — часть трафика через VPN (GeoIP / списки доменов): используйте вариант 2 (custom table + ip rule). Это именно то, что делает geo-split, и DHCP renewal его не затрагивает в принципе. Если есть доп. вводные, дайте знать... 1 hour ago, KeenTaur said: У меня эксперименты с неподдерживаемой уже ОС 2.16 Давайте сделаем проще, вот скрипт scripts/compat-check.sh, он проверяет апи на стоке 2.16 (и других). Пришлите выхлоп, пожалуйста. Поскипайте ***, если важное. Будет понятно, насколько обратно совместимо. Spoiler #!/opt/bin/sh # compat-check.sh — Keenetic firmware compatibility check for webui package. # Run on router to verify stock API availability before installing webui. # Covers every stock Keenetic dependency used by keenetic-entware-extras/webui. # # Usage: sh /opt/root/compat-check.sh # Output: text report with ✓/✗ for each check + raw samples for analysis. set -eu # ── Helpers ──────────────────────────────────────────────────────────── OK=0; WARN=0; FAIL=0 pass() { OK=$((OK + 1)); printf " ✓ %s\n" "$1"; } warn() { WARN=$((WARN+1)); printf " ⚠ %s\n" "$1"; } fail() { FAIL=$((FAIL+1)); printf " ✗ %s\n" "$1"; } hdr() { printf "\n━━━ %s ━━━\n" "$1"; } sample() { # Print indented sample (max N lines) _max="${2:-10}" echo "$1" | head -n "$_max" | sed 's/^/ | /' _total=$(echo "$1" | wc -l) if [ "$_total" -gt "$_max" ]; then printf " | ... (%s lines total)\n" "$_total" fi } # ══════════════════════════════════════════════════════════════════════ hdr "1. FIRMWARE INFO" # ══════════════════════════════════════════════════════════════════════ # 1a. ndmc "show version" — used by patch-stock-ui.sh and status.sh if command -v ndmc >/dev/null 2>&1; then pass "ndmc binary: found ($(command -v ndmc))" VER_OUT=$(ndmc -c "show version" 2>/dev/null) || VER_OUT="" if [ -n "$VER_OUT" ]; then pass "ndmc 'show version': responds" # We parse: title field FW_TITLE=$(echo "$VER_OUT" | awk '/title:/ { print $2; exit }') if [ -n "$FW_TITLE" ]; then pass "firmware title: $FW_TITLE" else warn "firmware title: not found in output" fi printf " raw sample:\n" sample "$VER_OUT" 15 else fail "ndmc 'show version': empty response" fi else fail "ndmc binary: not found" fi # 1b. /tmp/run/version.js — alternative firmware info if [ -f /tmp/run/version.js ]; then pass "/tmp/run/version.js exists" sample "$(cat /tmp/run/version.js)" 5 else warn "/tmp/run/version.js not found" fi # ══════════════════════════════════════════════════════════════════════ hdr "2. STOCK HTDOCS (WebUI Angular SPA)" # ══════════════════════════════════════════════════════════════════════ # NDMS 4.x/5.x: /usr/share/htdocs_, NDMS 2.x: may be /usr/share/htdocs (no underscore) HTDOCS="" for _htdocs_try in /usr/share/htdocs_ /usr/share/htdocs /www /tmp/www; do if [ -d "$_htdocs_try" ] && [ -f "$_htdocs_try/index.html" ]; then HTDOCS="$_htdocs_try" break fi done if [ -n "$HTDOCS" ]; then pass "htdocs dir: $HTDOCS exists" HTDOCS_COUNT=$(find "$HTDOCS" -maxdepth 1 -type f 2>/dev/null | wc -l) pass "htdocs root files: $HTDOCS_COUNT" printf " listing:\n" sample "$(ls -la "$HTDOCS"/ 2>/dev/null)" 30 else fail "htdocs dir: not found (tried /usr/share/htdocs_, /usr/share/htdocs, /www, /tmp/www)" HTDOCS="/usr/share/htdocs_" # fallback for subsequent checks # Check alternative locations for alt in /usr/share/htdocs /www /tmp/www; do if [ -d "$alt" ]; then warn "alternative htdocs: $alt found" printf " listing:\n" sample "$(ls -la "$alt"/ 2>/dev/null)" 15 fi done fi # 2a. main-*.js (Angular bundle — required by patch-stock-ui.sh) MAIN_JS=$(find "$HTDOCS" -maxdepth 1 -name "main-*.js" 2>/dev/null | head -1) if [ -n "$MAIN_JS" ]; then pass "main-*.js: found ($(basename "$MAIN_JS"))" JS_SIZE=$(wc -c < "$MAIN_JS") pass "main-*.js size: ${JS_SIZE} bytes" # Check for DashboardSection enum (used by patch detection) ENUM=$(grep -o '[A-Za-z][A-Za-z0-9]*={INTERNET:"INTERNET"' "$MAIN_JS" 2>/dev/null | head -1 | sed 's/=.*//') if [ -n "$ENUM" ]; then pass "DashboardSection enum: $ENUM" else warn "DashboardSection enum: not found (no patch set will match)" fi else fail "main-*.js: NOT FOUND — patch-stock-ui.sh will fail" fi # 2b. styles-*.css (stock CSS — used for theme consistency) STYLES_CSS=$(find "$HTDOCS" -maxdepth 1 -name "styles-*.css" 2>/dev/null | head -1) if [ -n "$STYLES_CSS" ]; then pass "styles-*.css: found ($(basename "$STYLES_CSS"))" else warn "styles-*.css: not found (custom dashboard will use fallback theme)" fi # 2c. index.html if [ -f "$HTDOCS/index.html" ]; then pass "index.html: found" # Check if it's an Angular SPA (various Angular markers across versions) if grep -q "app-root\|ng-app\|beasties-container\|main-.*\.js" "$HTDOCS/index.html" 2>/dev/null; then pass "index.html: Angular SPA detected" else warn "index.html: no Angular markers (old UI framework?)" fi printf " head sample:\n" sample "$(head -20 "$HTDOCS/index.html")" 20 else fail "index.html: not found in htdocs" fi # 2d. favicon (referenced by custom dashboard) if [ -f "$HTDOCS/assets/images/favicon/favicon.ico" ] || [ -f "$HTDOCS/favicon.ico" ]; then pass "favicon: found" else warn "favicon: not found (minor — custom dashboard icon will 404)" fi # ══════════════════════════════════════════════════════════════════════ hdr "3. ndmc 'show interface' (interface labels)" # ══════════════════════════════════════════════════════════════════════ # Used by: api-router.lua get_iface_labels() # We parse: id, description, type, link, address fields if command -v ndmc >/dev/null 2>&1; then IFACE_OUT=$(ndmc -c "show interface" 2>/dev/null) || IFACE_OUT="" if [ -n "$IFACE_OUT" ]; then pass "ndmc 'show interface': responds" IFACE_BLOCKS=$(echo "$IFACE_OUT" | grep -c '^Interface, name' 2>/dev/null || echo "0") pass "interface blocks: $IFACE_BLOCKS" # Check for key fields we parse for field in "id:" "description:" "type:" "link:" "address:"; do COUNT=$(echo "$IFACE_OUT" | grep -c "$field" 2>/dev/null || echo "0") if [ "$COUNT" -gt 0 ]; then pass "field '$field' present ($COUNT occurrences)" else warn "field '$field' NOT found" fi done # Show first interface block as sample printf " first interface block:\n" FIRST_BLOCK=$(echo "$IFACE_OUT" | awk '/^Interface, name/{n++} n==1{print} n==2{exit}') sample "$FIRST_BLOCK" 30 else fail "ndmc 'show interface': empty response" fi else fail "ndmc not available (skipping)" fi # ══════════════════════════════════════════════════════════════════════ hdr "4. ndmc 'show ip hotspot' (client list)" # ══════════════════════════════════════════════════════════════════════ # Used by: api-router.lua system_clients(), route-check.sh --from # We parse: host, mac, name, hostname, ip, active, id (Bridge*), description if command -v ndmc >/dev/null 2>&1; then HOTSPOT_OUT=$(ndmc -c "show ip hotspot" 2>/dev/null) || HOTSPOT_OUT="" if [ -n "$HOTSPOT_OUT" ]; then pass "ndmc 'show ip hotspot': responds" HOST_COUNT=$(echo "$HOTSPOT_OUT" | grep -c "host:" 2>/dev/null || echo "0") pass "hotspot entries (host:): $HOST_COUNT" for field in "mac:" "name:" "ip:" "active:" "hostname:"; do COUNT=$(echo "$HOTSPOT_OUT" | grep -c "$field" 2>/dev/null || echo "0") if [ "$COUNT" -gt 0 ]; then pass "field '$field' present ($COUNT)" else warn "field '$field' NOT found" fi done printf " first client block:\n" FIRST_CLIENT=$(echo "$HOTSPOT_OUT" | head -30) sample "$FIRST_CLIENT" 25 else warn "ndmc 'show ip hotspot': empty response (maybe no clients connected)" fi else fail "ndmc not available (skipping)" fi # ══════════════════════════════════════════════════════════════════════ hdr "5. STOCK HTTPD (auth proxy + RCI)" # ══════════════════════════════════════════════════════════════════════ # Used by: nginx.conf /auth (POST auth guard), /rci/ (stock UI proxy) # Detect stock httpd on port 80 HTTPD_LISTEN=$(netstat -tlnp 2>/dev/null | grep ":80 " | head -3) if [ -n "$HTTPD_LISTEN" ]; then pass "port 80: listening" sample "$HTTPD_LISTEN" 3 else warn "port 80: nothing listening (stock httpd may use different port)" fi # Try HTTP request to stock httpd ROUTER_IP=$(ip -4 addr show br0 2>/dev/null | awk '/inet / {split($2,a,"/"); print a[1]; exit}') if [ -z "$ROUTER_IP" ]; then ROUTER_IP="127.0.0.1" fi printf " router IP (br0): %s\n" "$ROUTER_IP" if command -v curl >/dev/null 2>&1; then # Test /auth endpoint (used for POST action authorization) AUTH_HEADERS=$(curl -s -D - -o /dev/null "http://${ROUTER_IP}:80/auth" 2>/dev/null) || AUTH_HEADERS="" AUTH_CODE=$(echo "$AUTH_HEADERS" | awk '/^HTTP\//{print $2}' | tail -1) if [ -n "$AUTH_CODE" ]; then pass "/auth response: HTTP $AUTH_CODE" # Detect auth type (NDMS 2.x=Digest, 4.x/5.x=session/cookie) AUTH_TYPE=$(echo "$AUTH_HEADERS" | grep -i "^WWW-Authenticate:" | head -1) if [ -n "$AUTH_TYPE" ]; then printf " auth type: %s\n" "$AUTH_TYPE" if echo "$AUTH_TYPE" | grep -qi "Digest"; then pass "auth method: Digest (NDMS 2.x style)" elif echo "$AUTH_TYPE" | grep -qi "Basic"; then pass "auth method: Basic" else pass "auth method: $(echo "$AUTH_TYPE" | sed 's/WWW-Authenticate: *//')" fi else pass "auth method: session/cookie (no WWW-Authenticate header)" fi else warn "/auth: no response from stock httpd" fi # Test /rci/ endpoint (used by stock Angular SPA) RCI_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://${ROUTER_IP}:80/rci/" 2>/dev/null) || RCI_CODE="000" if [ "$RCI_CODE" != "000" ]; then pass "/rci/ response: HTTP $RCI_CODE" # Get a small RCI sample RCI_SAMPLE=$(curl -s "http://${ROUTER_IP}:80/rci/" 2>/dev/null | head -c 500) if [ -n "$RCI_SAMPLE" ]; then printf " /rci/ sample (first 500 bytes):\n" sample "$RCI_SAMPLE" 10 fi else warn "/rci/: no response" fi # Test authenticated RCI call (show version via RCI, auth with web_password) # NDMS 2.x: Digest auth. NDMS 4.x+: x-ndw2-interactive (session-based). # Try unauthenticated first — some RCI endpoints may return data without auth. RCI_VER=$(curl -s "http://${ROUTER_IP}:80/rci/show/version" 2>/dev/null | head -c 500) if [ -n "$RCI_VER" ] && echo "$RCI_VER" | grep -q '"title"\|"release"\|"sandbox"'; then pass "/rci/show/version: responds with JSON (no auth needed)" sample "$RCI_VER" 5 elif [ -n "$RCI_VER" ] && echo "$RCI_VER" | grep -q "401\|Authorization"; then pass "/rci/show/version: requires auth (expected on 4.x/5.x)" else warn "/rci/show/version: unexpected response" [ -n "$RCI_VER" ] && sample "$RCI_VER" 5 fi elif command -v wget >/dev/null 2>&1; then AUTH_HEADERS=$(wget -q -S -O /dev/null "http://${ROUTER_IP}:80/auth" 2>&1) || AUTH_HEADERS="" AUTH_CODE=$(echo "$AUTH_HEADERS" | awk '/HTTP\//{print $2}' | tail -1) if [ -n "$AUTH_CODE" ]; then pass "/auth response: HTTP $AUTH_CODE" else warn "/auth: no response (wget)" fi else warn "curl/wget not available — skipping HTTP checks" fi # ══════════════════════════════════════════════════════════════════════ hdr "6. IPTABLES MANGLE (client routing policy)" # ══════════════════════════════════════════════════════════════════════ # Used by: api-router.lua system_clients() — reads _NDM_HOTSPOT_PREROUTING_MANGL if command -v iptables >/dev/null 2>&1; then pass "iptables: found" MANGLE_OUT=$(iptables -t mangle -S _NDM_HOTSPOT_PREROUTING_MANGL 2>/dev/null) || MANGLE_OUT="" if [ -n "$MANGLE_OUT" ]; then pass "_NDM_HOTSPOT_PREROUTING_MANGL chain: exists" RULE_COUNT=$(echo "$MANGLE_OUT" | wc -l) pass "mangle rules: $RULE_COUNT" # Check for specific patterns we parse MARK_COUNT=$(echo "$MANGLE_OUT" | grep -c "MARK --set-xmark" 2>/dev/null || echo "0") CONNNDM_COUNT=$(echo "$MANGLE_OUT" | grep -c "CONNNDMMARK" 2>/dev/null || echo "0") printf " MARK rules: %s, CONNNDMMARK rules: %s\n" "$MARK_COUNT" "$CONNNDM_COUNT" printf " sample:\n" sample "$MANGLE_OUT" 10 else warn "_NDM_HOTSPOT_PREROUTING_MANGL: chain not found (per-device routing unavailable)" fi else fail "iptables: not found" fi # ══════════════════════════════════════════════════════════════════════ hdr "7. IP ROUTING (core functionality)" # ══════════════════════════════════════════════════════════════════════ # Used by: all geo-split scripts, api-router.lua (system_interfaces, system_clients) for cmd in "ip -o link show" "ip -o -4 addr show" "ip rule show" "ip route show default"; do OUTPUT=$(eval "$cmd" 2>/dev/null) || OUTPUT="" if [ -n "$OUTPUT" ]; then pass "$cmd: works" sample "$OUTPUT" 5 else fail "$cmd: empty/failed" fi done # ip route show table all (used for fwmark→dev resolution) TABLE_ALL=$(ip route show table all 2>/dev/null | grep '^default' | head -5) || TABLE_ALL="" if [ -n "$TABLE_ALL" ]; then pass "ip route show table all (defaults): works" sample "$TABLE_ALL" 5 else warn "ip route show table all: no default routes" fi # ══════════════════════════════════════════════════════════════════════ hdr "8. LINUX /proc (system info — no stock dependency)" # ══════════════════════════════════════════════════════════════════════ # Used by: api-router.lua system_info() — pure Linux, should always work for procfile in /proc/sys/kernel/hostname /proc/uptime /proc/meminfo /proc/loadavg /proc/cpuinfo /proc/stat; do if [ -f "$procfile" ]; then pass "$procfile: exists" else fail "$procfile: NOT found" fi done # df -k /opt DF_OUT=$(df -k /opt 2>/dev/null | awk 'NR==2{print $2,$3,$4}') || DF_OUT="" if [ -n "$DF_OUT" ]; then pass "df -k /opt: $DF_OUT" else warn "df -k /opt: failed" fi # ══════════════════════════════════════════════════════════════════════ hdr "9. ENTWARE PACKAGES" # ══════════════════════════════════════════════════════════════════════ # Check required packages for webui # nginx binary check first (nginx-mod-lua pulls nginx as dependency) if [ -x /opt/sbin/nginx ]; then NGINX_VER=$(/opt/sbin/nginx -v 2>&1 | head -1) pass "nginx binary: $NGINX_VER" else fail "nginx binary: /opt/sbin/nginx not found" fi for pkg in nginx-mod-lua logrotate; do PKG_STATUS=$(opkg status "$pkg" 2>/dev/null | grep "Status:" | head -1) || PKG_STATUS="" if echo "$PKG_STATUS" | grep -q "installed"; then VER=$(opkg status "$pkg" 2>/dev/null | grep "Version:" | head -1 | awk '{print $2}') pass "$pkg: installed ($VER)" else fail "$pkg: NOT installed" fi done # Check for ip-full (needed for batch route loading) for pkg in ip-full iproute2; do PKG_STATUS=$(opkg status "$pkg" 2>/dev/null | grep "Status:" | head -1) || PKG_STATUS="" if echo "$PKG_STATUS" | grep -q "installed"; then VER=$(opkg status "$pkg" 2>/dev/null | grep "Version:" | head -1 | awk '{print $2}') pass "$pkg: installed ($VER)" break fi done # ══════════════════════════════════════════════════════════════════════ hdr "10. NDM HOOK DIRECTORIES" # ══════════════════════════════════════════════════════════════════════ # Used by: geo-split ndm-hook.sh (ifstatechanged), smartdns-redirect netfilter-hook.sh NDM_BASE="/opt/etc/ndm" if [ -d "$NDM_BASE" ]; then pass "ndm hook base: $NDM_BASE exists" else fail "ndm hook base: $NDM_BASE NOT found" fi for hookdir in ifstatechanged.d netfilter.d wan.d ifipchanged.d schedule.d fs.d button.d usb.d; do if [ -d "$NDM_BASE/$hookdir" ]; then HOOK_COUNT=$(find "$NDM_BASE/$hookdir" -maxdepth 1 -type f -o -type l 2>/dev/null | wc -l) pass "$hookdir: exists ($HOOK_COUNT scripts)" else warn "$hookdir: NOT found" fi done # ══════════════════════════════════════════════════════════════════════ hdr "11. OPKG FEEDS & ENTWARE ENV" # ══════════════════════════════════════════════════════════════════════ # Entware version and feed architecture if [ -f /opt/etc/opkg.conf ]; then pass "opkg.conf: exists" FEED_ARCH=$(grep -o 'entware-[a-z0-9_]*' /opt/etc/opkg.conf 2>/dev/null | head -1) if [ -n "$FEED_ARCH" ]; then pass "feed arch: $FEED_ARCH" fi printf " feeds:\n" sample "$(grep -v '^#' /opt/etc/opkg.conf 2>/dev/null | grep -v '^$')" 10 else fail "opkg.conf: not found (Entware not installed?)" fi # Entware base dir if [ -d /opt/etc/init.d ]; then INIT_COUNT=$(find /opt/etc/init.d -maxdepth 1 -name "S*" 2>/dev/null | wc -l) pass "Entware init.d: $INIT_COUNT startup scripts" else warn "Entware init.d: not found" fi # ══════════════════════════════════════════════════════════════════════ hdr "12. ARCHITECTURE & KERNEL" # ══════════════════════════════════════════════════════════════════════ ARCH=$(uname -m 2>/dev/null) || ARCH="unknown" KERNEL=$(uname -r 2>/dev/null) || KERNEL="unknown" pass "arch: $ARCH" pass "kernel: $KERNEL" # BusyBox version BB_VER=$(busybox 2>&1 | head -1) || BB_VER="not found" pass "busybox: $BB_VER" # Shell check (ash has no --version; use readlink to identify) if [ -L /opt/bin/sh ]; then SHELL_TARGET=$(readlink /opt/bin/sh 2>/dev/null || echo "unknown") pass "/opt/bin/sh -> $SHELL_TARGET" elif [ -f /opt/bin/sh ]; then pass "/opt/bin/sh: exists (not a symlink)" else warn "/opt/bin/sh: not found" fi # ══════════════════════════════════════════════════════════════════════ hdr "SUMMARY" # ══════════════════════════════════════════════════════════════════════ printf "\n" printf " ✓ Pass: %d\n" "$OK" printf " ⚠ Warn: %d\n" "$WARN" printf " ✗ Fail: %d\n" "$FAIL" printf "\n" if [ "$FAIL" -eq 0 ]; then printf " All critical checks passed. webui should work.\n" elif [ "$FAIL" -le 2 ]; then printf " Minor issues detected. webui /custom/ dashboard may work with workarounds.\n" else printf " Multiple failures. webui likely needs adaptation for this firmware.\n" fi printf "\n Please share this full output for analysis.\n\n" 2 hours ago, KeenTaur said: С intel.com у меня ситуация как и у dimsok. (по последним данным не работает только первая страница, загрузки работают.) Но вопрос интересный... 2 hours ago, KeenTaur said: Скриншот с сайтом и трассировкой через туннель и url. В конфигурации geoip сейчас только гео-зона ru. Все говорит о том, что vps кривоват... Spoiler Анализ проблемы KeenTaur с intel.com Tracert из скриншота (Windows) tracert -d www.intel.com → e7842.dsca.akamaiedge.net [23.210.254.130] Hop 1: 10.5.33.1 ← роутер (LAN gateway) Hop 2: 10.236.17.0 ← VPN туннель (RFC1918, 10.0.0.0/8) Hop 3: 172.29.172.1 ← VPN-сервер внутренняя сеть (RFC1918, 172.16.0.0/12) Hop 4: 100.100.1.1 ← CGNAT российского ISP (RFC 6598, 100.64.0.0/10) Диагноз VPN-туннель выходит в Россию. Hop 4 (100.100.1.1) — это CGNAT (Carrier-Grade NAT по RFC 6598, диапазон 100.64.0.0/10), характерный для российских ISP (Билайн, МТС, Ростелеком). Цепочка: www.intel.com резолвится в 23.210.254.130 (Akamai CDN, NetName: AIBV, Нидерланды) IP 23.210.254.130 не в российских GeoIP-диапазонах → geo-split НЕ маршрутизирует его через ISP Трафик идёт через default route → VPN-туннель VPN-туннель выходит через российский ISP (CGNAT 100.100.x.x) Akamai видит запрос с российского IP → Intel перенаправляет на ru-banner.html (санкционная страница) Это НЕ баг geo-split Geo-split работает корректно: Российские IP → маршрутизируются через ISP напрямую (таблицы 1000/1001) Все остальные IP (включая Akamai 23.210.254.130) → default route → VPN Проблема в том, что VPN-сервер сам находится в России (или выходит через российского провайдера). Intel.com увидит российский IP независимо от того, идёт трафик напрямую или через VPN. Про "intel.com без www не открывается" Отдельная проблема: intel.com (без www) делает HTTP 301 redirect → www.intel.com. Если редирект сам идёт через российский exit-point, Intel сразу перебрасывает на ru-banner.html ещё до загрузки основного сайта. Что делать Вариант Описание 1. VPN за рубежом Нужен VPN-сервер с exit node ВОВНЕ России (Германия, Нидерланды, Финляндия). Без этого маршрутизация через VPN бессмысленна для обхода гео-блокировок 2. Добавить intel.com в домены Если у пользователя ЕСТЬ второй VPN (за границей) или ISP выдаёт нероссийский IP — добавить intel.com / www.intel.com в domains.txt, чтобы трафик шёл через ISP 3. Понять назначение VPN VPN в России имеет смысл для шифрования трафика от ISP или обхода DPI — но НЕ для обхода гео-блокировок иностранных сайтов Вывод Geo-split с GEO_ZONE=ru маршрутизирует российские сайты через ISP. Для иностранных сайтов (intel.com, Akamai CDN) трафик идёт через VPN — но если VPN сам выходит в России, проблема не решается. KeenTaur нужен VPN с exit node за пределами РФ. 🤷♂️ Quote
0xkee Posted July 13 Author Posted July 13 27 minutes ago, RWH said: @KeenTaur, у меня нет такой проблемы, нормально открывается intel.com +1 ⚠️ хинт: В продаже существуют типа иностранные vps которые не совсем иностранные... Цена в ~250 руб/мес - верный признак, нормальные где-то от 500-600 руб и более Quote
0xkee Posted July 13 Author Posted July 13 (edited) А ещё бывает так: Spoiler Что известно VPN exit IP: #.#.#.# whatismyipaddress.com: Helsinki, Finland, *** ✓ Netflix: работает ✓ ChatGPT: работает ✓ Intel.com: показывает санкционный баннер ✗ WHOIS анализ IP #.#.#.# Уровень 1 — /24 sub-allocation (IPXO marketplace): inetnum: #.#.#.# - #.#.#.# netname: ipxo ← IP leasing marketplace! country: FI Уровень 2 — /15 parent block (оригинальный владелец): inetnum: #.#.#.# - #.#.#.# netname: US-*** org-name: *** country: US Уровень 3 — AS announcement: ***: ***-AS org-name: **** country: PL (***, Польша) Корневая причина: IPXO leased IP с "грязной" репутацией IPXO — это маркетплейс аренды IP-адресов. Владелец блока (***, US) сдаёт /24 через IPXO, арендатор (***, Польша) анонсирует его через AS*** из финского датацентра. Проблема: разные GeoIP-базы классифицируют этот IP по-разному: Сервис GeoIP база Результат whatismyipaddress.com RIPE WHOIS + BGP Finland ✓ Netflix Собственная + MaxMind Finland ✓ (или не блокирует FI) ChatGPT/OpenAI Вероятно MaxMind Finland ✓ Intel.com (*** CDN) *** GeoIP 🇷🇺 Russia ✗ И как результат - нет интела (первой страницы) Edited July 13 by 0xkee Quote
mrGhotius Posted July 13 Posted July 13 (edited) 3 часа назад, KeenTaur сказал: С intel.com у меня ситуация как intel.se добавляли? Мне помогло обойти заглушку, хоть и резолвятся на одном и том же ip. И ЧВС у меня не в Швеции UPD. Зато DoT и DoH в Швеции. Edited July 13 by mrGhotius Quote
0xkee Posted July 13 Author Posted July 13 (edited) Вопрос: Нужен ли функционал для проверки качества туннеля/ей, на прикладном уровне («end-to-end accessibility check») - какие ресурсы (сайты и тд) доступны через туннель, а какие нет? Какой-то общий список + персональные настройки. Поставьте, пожалуйста, этому сообщению upvote, если надо. Edited July 13 by 0xkee 5 Quote
KeenTaur Posted July 14 Posted July 14 12 часов назад, mrGhotius сказал: intel.se добавляли? да вроде с конфигурацией "геозона ru напрямую через провайдера, все остальное в туннель" не требуется. Склонен думать, что туннель хоть и выходит, ну пусть будет, в /Средней/ Азии, но интелу не нравится этот облачный провайдер. Остальное, в основном, для @0xkee 13 часов назад, 0xkee сказал: VPN-туннель выходит через российский ISP (CGNAT 100.100.x.x) Вот с выделенным не согласен. Просмотрел несколько сервисов whois, диапазон 100.64.0.0 - 100.127.255.255 это Shared Address Space (Общее адресное пространство), которое "может использоваться только в сетях поставщиков услуг или на маршрутизирующем оборудовании, которое способно выполнять преобразование адресов между интерфейсами маршрутизатора, когда адреса идентичны на двух разных интерфейсах". Про российских ISP не увидел ничего. Просто внутренняя кухня. Дальше в моей трассировке были бы видны реальные адреса провадера, потому она и прервана. По web-ui 13 часов назад, 0xkee сказал: Давайте сделаем проще, вот скрипт scripts/compat-check.sh, он проверяет апи на стоке 2.16 (и других). Пришлите выхлоп, пожалуйста. Глянул листинг скрипта Гм, ниче-се проще Но я постараюсь, но попозже. Спасибо! А начну вот с этого: 13 часов назад, 0xkee сказал: 1. Приоритеты подключений через Keenetic UI (рекомендуемый) Если нужно весь трафик пустить через awg0 — настройте приоритет подключений в веб-интерфейсе Keenetic: Не подходит, или я не знаю как. В 2.16 Keenetic в веб-интерфейсе не видит awg0 с asc2.0, созданного в Entware. Механизм opkgtun появился позже. 13 часов назад, 0xkee сказал: 2. Custom routing table + ip rule (надёжный обходной путь) а вот это посмотрю-попробую. Бланодарю! Quote
0xkee Posted July 14 Author Posted July 14 53 minutes ago, KeenTaur said: Вот с выделенным не согласен. ... Дальше в моей трассировке были бы видны реальные адреса провадера, потому она и прервана. Ок, тут ещё тема всплыла, браузеры типа хрома (а это почти все), сильно кешируют dns иногда даже используют свои... тот же интел лучше ещё попроверять с другого браузера/компа/телефона в этой же сети... возможно конкретный браузер ещё не "отлип" от старого cdn Quote
KeenTaur Posted July 14 Posted July 14 5 минут назад, 0xkee сказал: тот же интел лучше ещё попроверять с другого браузера/компа/телефона в этой же сети... возможно конкретный браузер ещё не "отлип" от старого cdn Проблема в проверках интела, он явно считает доступные мне vps нерукопожатными geoip не при чем. Кроме этого роутера и ноута за ним, я проверил с утра через туннели, установленные прямо со смартфона в режиме весь трафик через туннели на двух vps с разными внешними адресами, но у одного провайдера. Quote
cryoPanda Posted July 17 Posted July 17 Доброго времени суток! Пара вопросов: 1) Будет ли поддержка Netcraze 5.1.1? Ради интереса обновил прошивку. На данный момент всё работает, кроме вебки 2) Если добавлять домены в domains.txt, то для полного исключения достаточно так написать *.dion.vc Или без wildcard также домен нужно указать? dion.vc *.dion.vc Quote
RWH Posted July 17 Posted July 17 (edited) 1 час назад, cryoPanda сказал: 1) Будет ли поддержка Netcraze 5.1.1? Ради интереса обновил прошивку. На данный момент всё работает, кроме вебки @cryoPanda все модули прекрасно работают, попробуйте переустановить модуль WebUI через деинсталяцию и новую установку Спойлер Ответ на второй вопрос находится по следующему пути /opt/keenetic-entware-extras/geo-split-data/lists/ru-whitelist.txt Спойлер ru-whitelist.txt Edited July 17 by RWH 1 Quote
0xkee Posted July 28 Author Posted July 28 (edited) Всем привет! Сделана первая версия пакета проверки связности сети net-check. Пока только CLI версия. Веб версия будет, но вебка реально использует cli компоненты, поэтому желательно сначала оценить cli. (status.sh тоже не доделан.) Просьба ко всем, кому интересен этот пакет, посмотреть и покомментировать. Что нравиться/не нравиться, что убрать/добавить и так далее! Сейчас работают две команды: /opt/keenetic-entware-extras/net-check/scripts/net-check.sh - 9 тестов проверки доменов пачками, есть конфиги (в первом приближении), есть usage (без параметров) /opt/keenetic-entware-extras/net-check/scripts/domain-check.sh - несколько тестов для проверки одного домена из командной строки Edited: в плане определения аномалий, могут быть ложноположительные и ложноотрицательные оценки; также связность сети может немного меняться от теста к тесту, это естественное поведение сети (но если Вам кажется не очень надежные результаты проверок - дайте знать) Edited July 28 by 0xkee 3 Quote
0xkee Posted July 29 Author Posted July 29 ... В "[5/9] DNS Leak Test (Layer 7)" ложно положительные срабатывания, есть баг, исправляем Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.