--- title: Rasputin OpenWrt router created: '2026-07-22' updated: '2026-10-30' tags: - openwrt - router - rasputin - networking - vless - cloudflared --- # Rasputin R5S OpenWrt router Rasputin — LAN-роутер OpenWrt: `192.168.6.1/24`, WAN `eth0` — `192.168.2.157/24`, шлюз `192.168.2.2`. Клиентский TCP-трафик идёт через `tun0` → `badvpn-tun2socks` → SOCKS Xray → TrueNAS → reverse-туннель → Kraken → интернет. Для локальных сетей аплинка и Mac `cloudflared` QUIC действуют прямые исключения. DNS клиентов обслуживает отдельная служба `clientdns` через тот же туннель. Системный dnsmasq и DNS самого роутера работают отдельно. SSH: `ssh root@192.168.6.1`. ## Identity | Field | Value | |---|---| | Hostname | `Rasputin` | | Hardware | FriendlyElec NanoPi R5S | | Board | `friendlyarm,nanopi-r5s` | | CPU | ARMv8 Processor rev 0 | | OpenWrt | `24.10.2`, revision `r28739-d9340319c6` | | Target | `rockchip/armv8` | | Kernel | `6.6.93` | | Rootfs | squashfs | | LAN IP | `192.168.6.1/24` | | IPv6 ULA prefix | `fd3b:153e:9be9::/48` | ## Network Topology ```text LAN clients -> br-lan / 192.168.6.0/24 -> policy routing -> most LAN traffic: table vpn -> tun0 -> badvpn-tun2socks -> Xray SOCKS -> VLESS -> selected exceptions: table main -> WAN device ``` В зоне `wan` находятся `eth0` и `usb0`; активный аплинк — `eth0`. ```text default via 192.168.2.2 dev eth0 proto static src 192.168.2.157 ``` Аплинк: Rasputin → `192.168.2.2` → GPON `192.168.0.1`. Туннель подключается к TrueNAS `192.168.2.197:8443` через локальную сеть аплинка. VPN table: ```text default dev tun0 scope link ``` ## Interfaces ### LAN `br-lan` bridges: ```text eth1 eth2 phy0-ap0 ``` LAN config: ```text network.lan=interface network.lan.device='br-lan' network.lan.proto='static' network.lan.ipaddr='192.168.6.1' network.lan.netmask='255.255.255.0' network.lan.ip6assign='60' network.lan.dns='8.8.8.8' ``` ### WAN `eth0`: ```text network.wan=interface network.wan.device='eth0' network.wan.proto='dhcp' network.wan.peerdns='0' network.wan.type='bridge' ``` `usb0`: ```text network.usbwan=interface network.usbwan.proto='dhcp' network.usbwan.device='usb0' ``` Firewall `wan` zone includes: ```text wan wan6 wwan usbwan ``` ### VPN `tun0` is declared as an unmanaged interface: ```text network.vpn=interface network.vpn.proto='none' network.vpn.device='tun0' ``` `tun0` is created/used by `badvpn-tun2socks`, launched from `/etc/init.d/tunvpn`. ## DHCP And Static Leases DHCP server: ```text dhcp.lan=dhcp dhcp.lan.interface='lan' dhcp.lan.start='100' dhcp.lan.limit='150' dhcp.lan.leasetime='12h' dhcp.lan.dhcp_option='6,192.168.6.1' dhcp.lan.dhcpv4='server' dhcp.lan.dhcpv6='server' dhcp.lan.ra='server' dhcp.lan.ra_slaac='1' dhcp.lan.ra_flags='managed-config' 'other-config' ``` Static DHCP reservations added 2026-07-14 for the Mac: ```text dhcp.@host[0].name='mbp-black-eth' dhcp.@host[0].mac='00:e0:4c:68:04:88' dhcp.@host[0].ip='192.168.6.104' dhcp.@host[1].name='mbp-black-wifi' dhcp.@host[1].mac='66:02:b1:8b:2f:94' dhcp.@host[1].ip='192.168.6.173' ``` Backup made before this change: ```text /etc/config/dhcp.bak.20260714-195408 ``` ## Клиентский DNS через туннель DHCP выдаёт клиентам `192.168.6.1`: ```text dhcp.lan.dhcp_option='6,192.168.6.1' ``` ```text LAN-клиент → адрес Rasputin:53 (UDP/TCP) → firewall dstnat_lan → redirect :1053 → отдельный dnsmasq службы clientdns → локальные имена: системный dnsmasq 127.0.0.1:53 → внешний DNS: отдельный Xray 127.0.0.1:1054 → SOCKS основного Xray 127.0.0.1:1080 → VLESS → TrueNAS → reverse-туннель → Kraken → Cloudflare: A/AAAA через DoH; другие типы через DNS-over-TCP ``` `clientdns` не меняет upstream системного dnsmasq и встроенный DNS основного Xray. Системный dnsmasq слушает порт 53 и использует `8.8.8.8` из `/tmp/resolv.conf.d/resolv.conf.auto`; запросы самого роутера не перенаправляются. DNS аплинка `192.168.2.2` настроен на `1.1.1.1` и полученный по WAN `192.168.0.1`. Обрабатываются только входящие из LAN TCP/UDP-запросы к DNS-адресам Rasputin: `192.168.6.1`, `fd3b:153e:9be9::1` и `fe80::34ae:87ff:fe04:8dd`. Запросы к произвольным внешним DNS-адресам это правило не перехватывает. При смене LAN IPv6-адресов нужно обновить `/etc/clientdns/redirect.nft`. Общая передача клиентского UDP через текущий tun2socks не настроена. Для DNS это не требуется: клиентский UDP заканчивается на Rasputin, а дальше запрос идёт по TCP через прокси. При недоступности основного Xray/reverse-туннеля внешний клиентский DNS также недоступен; прямого fallback через WAN нет. ### Файлы и параметры clientdns `/etc/init.d/clientdns` — отдельная служба procd с двумя экземплярами: `doh` (Xray) и `resolver` (dnsmasq). Автозапуск включён, `START=99`, `STOP=10`; для обоих процессов настроен `respawn 3600 5 5`. Ссылки автозапуска: `/etc/rc.d/S99clientdns` и `/etc/rc.d/K10clientdns`. После загрузки внешний клиентский DNS становится доступен, когда основной Xray и reverse-туннель готовы принимать запросы. Параметры и правила firewall сохранены в `/etc`; проверка enable/disable выполняется без остановки процессов. `/etc/clientdns/dnsmasq.conf`: ```ini # Dedicated client resolver; the system dnsmasq remains on port 53. port=1053 interface=br-lan interface=lo bind-dynamic no-resolv no-poll no-hosts cache-size=1000 edns-packet-max=1232 server=127.0.0.1#1054 # Preserve the existing router's local name service. server=/lan/local/127.0.0.1#53 server=//127.0.0.1#53 server=/6.168.192.in-addr.arpa/ip6.arpa/127.0.0.1#53 user=dnsmasq group=dnsmasq log-facility=/tmp/clientdns-dnsmasq.log ``` Локальные домены `lan`, `local`, имена без доменной части, обратные запросы для `192.168.6.0/24` и `ip6.arpa` передаются прежнему dnsmasq. Клиентский экземпляр не раздаёт DHCP. `/etc/clientdns/xray.json`: ```json { "log": { "loglevel": "warning", "access": "/tmp/clientdns-xray-access.log", "error": "/tmp/clientdns-xray-error.log" }, "inbounds": [ { "tag": "clientdns-in", "listen": "127.0.0.1", "port": 1054, "protocol": "dokodemo-door", "settings": { "address": "1.1.1.1", "port": 53, "network": "tcp,udp" } } ], "outbounds": [ { "tag": "tunnel", "protocol": "socks", "settings": { "servers": [ { "address": "127.0.0.1", "port": 1080 } ] } }, { "tag": "dns-out", "protocol": "dns", "settings": { "network": "tcp", "address": "1.1.1.1", "port": 53, "nonIPQuery": "skip" }, "proxySettings": { "tag": "tunnel" } } ], "routing": { "domainStrategy": "AsIs", "rules": [ { "type": "field", "inboundTag": [ "clientdns-in" ], "outboundTag": "dns-out" }, { "type": "field", "inboundTag": [ "clientdns-upstream" ], "outboundTag": "tunnel" } ] }, "dns": { "servers": [ "https://1.1.1.1/dns-query" ], "queryStrategy": "UseIP", "tag": "clientdns-upstream" } } ``` У отдельного Xray нет собственного VLESS-подключения: он использует существующий SOCKS `127.0.0.1:1080`. A/AAAA разрешаются через `https://1.1.1.1/dns-query`; остальные типы (`MX`, `TXT`, `HTTPS` и другие) передаются к `1.1.1.1:53` по TCP через тот же SOCKS. `UseIP` относится только к отдельному DNS-процессу; основной Xray сохраняет `UseIPv4`. Подключение правил в `/etc/config/firewall`: ```uci config include 'clientdns_redirect' option type 'nftables' option path '/etc/clientdns/redirect.nft' option chain 'dstnat_lan' option position 'chain-prepend' ``` `/etc/clientdns/redirect.nft`: ```nft ip daddr 192.168.6.1 meta l4proto { tcp, udp } th dport 53 counter redirect to :1053 comment "clientdns-v4" ip6 daddr { fd3b:153e:9be9::1, fe80::34ae:87ff:fe04:8dd } meta l4proto { tcp, udp } th dport 53 counter redirect to :1053 comment "clientdns-v6" ``` ### Управление и диагностика clientdns На роутере: ```sh /etc/init.d/clientdns status /etc/init.d/clientdns enabled ubus call service list '{"name":"clientdns"}' /usr/bin/xray run -test -config /etc/clientdns/xray.json < /dev/null /usr/sbin/dnsmasq --test -C /etc/clientdns/dnsmasq.conf netstat -lnp | grep -E ':(1053|1054) ' uci show firewall.clientdns_redirect nft list chain inet fw4 dstnat_lan ``` Оба экземпляра procd должны быть `running`; при клиентских запросах растут счётчики `clientdns-v4`/`clientdns-v6`. Проверка обработчика на роутере и отдельной маршрутизации локальных имён: ```sh nslookup www.youtube.com 127.0.0.1:1053 nslookup rasputin.lan 127.0.0.1:1053 ``` Проверка с LAN-клиента через стандартный DNS-порт: ```sh dig @192.168.6.1 www.youtube.com A dig +tcp @192.168.6.1 www.youtube.com A dig @192.168.6.1 rasputin.lan A dig @192.168.6.1 gmail.com MX dig @192.168.6.1 cloudflare.com TYPE65 curl -4 -fsS -o /dev/null --max-time 20 -w 'HTTP %{http_code}\n' https://www.youtube.com ``` `TYPE65` — запись HTTPS; такой синтаксис подходит для штатного macOS dig. Ожидаются ответы DNS и `HTTP 200`. Запрос на роутере к `127.0.0.1:53` проверяет системный DNS, а не клиентский путь. Логи отдельной службы: ```sh tail -100 /tmp/clientdns-dnsmasq.log tail -100 /tmp/clientdns-xray-error.log tail -100 /tmp/clientdns-xray-access.log grep '1.1.1.1:443' /tmp/xray-access.log | tail -10 ``` Перезапуск только клиентского DNS: ```sh /etc/init.d/clientdns restart ``` ### Отключение и откат clientdns Подключиться по IP: `ssh root@192.168.6.1`. На роутере выполнить: ```sh sh /etc/clientdns/rollback.sh ``` Эквивалентные ручные команды: ```sh uci -q delete firewall.clientdns_redirect uci commit firewall /etc/init.d/firewall reload /etc/init.d/clientdns stop /etc/init.d/clientdns disable ``` После этого клиентские запросы снова обрабатывает исходный dnsmasq на порту 53. Менять DHCP или настройки клиентов не нужно. Возвращается обычный DNS через WAN, вместе с его ограничениями. Копия скрипта отката и исходные конфиги находятся на роутере в `/root/clientdns-backup-20261030-134826/`: `rollback.sh`, `firewall`, `firewall.uci`, `dhcp`, `xray-config.json`. Для отключения clientdns достаточно удалить его отдельный блок firewall; восстановление всего firewall из копии может затереть другие последующие изменения. При полном удалении сначала выполнить откат, затем удалить `/etc/init.d/clientdns` и `/etc/clientdns/`. Нельзя удалять `redirect.nft`, пока на него ссылается включённый блок firewall. ## VLESS / Xray Stack Packages present include: ```text badvpn xray-core v2ray-core v2ray-extra v2ray-geoip v2ray-geosite v2raya luci-app-v2raya sing-box nftables-json iptables-nft ``` Runtime processes: ```text badvpn-tun2socks --tundev tun0 --netif-ipaddr 10.99.0.2 --netif-netmask 255.255.255.252 --socks-server-addr 127.0.0.1:1080 --loglevel none /usr/bin/xray run -config /etc/xray/config.json -format json ``` `v2raya` is installed, but the observed UCI service config previously had `enabled='0'`. The active traffic path is the custom `tunvpn` service plus direct Xray config. ### Xray Config Основной Xray управляется OpenWrt procd через `/etc/init.d/xray`. Конфиг: `/etc/xray/config.json`; UCI: `/etc/config/xray`; ресурсы: `/usr/share/xray`. Автозапуск включён. ```text xray.enabled.enabled='1' xray.config.conffiles='/etc/xray/config.json' xray.config.datadir='/usr/share/xray' xray.config.format='json' ``` SOCKS-вход: `127.0.0.1:1080`. Выход: VLESS поверх WebSocket/TLS к TrueNAS `192.168.2.197:8443`, SNI/Host `vpn.mallexxx.duckdns.org`, путь `/vless`. UUID в документации скрыт; брать его из действующего конфига. ```json { "log": { "loglevel": "debug", "access": "/tmp/xray-access.log", "error": "/tmp/xray-error.log" }, "inbounds": [ { "tag": "socks-in", "listen": "127.0.0.1", "port": 1080, "protocol": "socks" } ], "outbounds": [ { "tag": "proxy", "protocol": "vless", "settings": { "vnext": [ { "address": "192.168.2.197", "port": 8443, "users": [ { "id": "", "encryption": "none" } ] } ] }, "streamSettings": { "network": "ws", "security": "tls", "tlsSettings": { "serverName": "vpn.mallexxx.duckdns.org" }, "wsSettings": { "path": "/vless", "headers": { "Host": "vpn.mallexxx.duckdns.org" } } } }, { "tag": "direct", "protocol": "freedom" }, { "tag": "block", "protocol": "blackhole" } ], "routing": { "domainStrategy": "IPIfNonMatch", "rules": [ { "type": "field", "protocol": [ "bittorrent" ], "outboundTag": "block" } ] }, "dns": { "servers": [ { "address": "223.5.5.5", "port": 53 }, { "address": "8.8.8.8", "port": 53 } ], "queryStrategy": "UseIPv4" } } ``` ### Перезапуск и диагностика Xray Команды выполняются на Rasputin после `ssh root@192.168.6.1`. Проверить конфиг перед перезапуском; успешный результат — `Configuration OK.`: ```sh /usr/bin/xray run -test -config /etc/xray/config.json < /dev/null ``` Перезапустить только основную службу Xray: ```sh /etc/init.d/xray restart /etc/init.d/xray status ``` Проверить автозапуск и состояние процесса под procd: ```sh /etc/init.d/xray enabled ubus call service list '{"name":"xray"}' uci show xray netstat -lnp | grep '127.0.0.1:1080' ``` `status` должен выводить `running`; `enabled` завершается с кодом 0, если автозапуск включён. В ответе ubus у экземпляра `xray` должно быть `"running": true`; TCP-порт `127.0.0.1:1080` должен слушаться. Логи: ```sh tail -100 /tmp/xray-error.log tail -100 /tmp/xray-access.log logread -e xray ``` Файлы в `/tmp` очищаются при перезагрузке. В access-log `[socks-in >> proxy]` означает передачу запроса в основной VLESS-выход. Сквозная проверка основного прокси с удалённым разрешением имени: ```sh curl -4 -fsS --max-time 20 --socks5-hostname 127.0.0.1:1080 https://api.ipify.org curl -4 -fsS -o /dev/null --max-time 20 --socks5-hostname 127.0.0.1:1080 -w 'HTTP %{http_code}\n' https://www.youtube.com ``` Первый запрос должен показать текущий внешний IP Kraken, второй — `HTTP 200`. IP Kraken динамический; для сравнения получить его непосредственно на Kraken. `xray`, `tunvpn` и `clientdns` — разные службы. Перезапуск `xray` не пересоздаёт `tun0` и policy routing; для их проверки использовать `ip rule show`, `ip route show table vpn` и `ip link show tun0`. Команда `/etc/init.d/tunvpn restart` перезапускает tun2socks и маршрутизацию. Клиентский DNS перезапускается через `/etc/init.d/clientdns restart`. Не использовать `killall xray`: он остановит также отдельный процесс Xray службы `clientdns`. ## `tunvpn` Service File: ```text /etc/init.d/tunvpn ``` Purpose: - Start `badvpn-tun2socks`. - Populate routing table `vpn` with default route through `tun0`. - Add policy rules so LAN traffic uses the VPN table. - Add explicit Cloudflare Tunnel QUIC bypass rules for the Mac. Current script: ```sh #!/bin/sh /etc/rc.common START=95 STOP=10 start() { badvpn-tun2socks --tundev tun0 --netif-ipaddr 10.99.0.2 --netif-netmask 255.255.255.252 --socks-server-addr 127.0.0.1:1080 --loglevel none & sleep 2 ip route replace default dev tun0 table vpn # cloudflared QUIC direct bypass for Mac Ethernet/Wi-Fi ip rule add priority 98 from 192.168.6.173 ipproto udp dport 7844 lookup main 2>/dev/null ip rule add priority 99 from 192.168.6.104 ipproto udp dport 7844 lookup main 2>/dev/null ip rule add iif br-lan lookup vpn priority 101 } stop() { killall badvpn-tun2socks ip rule del priority 98 2>/dev/null ip rule del priority 99 2>/dev/null ip rule del iif br-lan lookup vpn 2>/dev/null ip route flush table vpn } ``` Backup made before the Cloudflare direct-bypass update: ```text /etc/init.d/tunvpn.bak.20260714-195530 ``` ## Policy Routing Current rules (снято 2026-09-22, живая система): ```text 0: from all lookup local 98: from 192.168.6.173 ipproto udp dport 7844 lookup main 99: from 192.168.6.104 ipproto udp dport 7844 lookup main 100: from all to 192.168.2.0/24 lookup main 100: from all to 192.168.0.0/24 lookup main 101: from all iif br-lan lookup vpn 32766: from all lookup main 32767: from all lookup default ``` Две строки с `priority 100` — это нормально (обе добавлены вручную, `192.168.0.0/24` — 2026-09-17). Meaning: - Mac Wi-Fi `192.168.6.173` UDP/7844 goes direct through `main`. - Mac Ethernet `192.168.6.104` UDP/7844 goes direct through `main`. - Traffic to upstream LAN `192.168.2.0/24` goes direct through `main`. - Traffic to GPON `192.168.0.0/24` goes direct through `main` (исправлено 2026-09-17). - 🔴 Traffic to `192.168.1.0/24` (Кра́кен, ТВ, ASUS) is **NOT** covered by any rule `100` (2026-09-22) → falls through to rule `101` → `table vpn` → `tun0`. Доступ к Кра́кену/ТВ из LAN `192.168.6.0/24` не работает. См. раздел «🔴 Транзит из LAN в 192.168.1.0/24». - All other traffic entering from LAN bridge `br-lan` uses `table vpn`. - `table vpn` sends default traffic through `tun0`. `/etc/rc.local` adds the upstream-LAN bypass: ```sh # Put your custom commands here that should be executed once # the system init finished. By default this file does nothing. ip rule add to 192.168.2.0/24 lookup main priority 100 exit 0 ``` ⚠️ Здесь только `192.168.2.0/24`. Строки для `192.168.0.0/24` нет — это и есть корневая причина сломанного транзита в GPON-сегмент. После ребута текущий `ip rule 100` пересоздаётся именно из этого файла. ## Firewall Model Default firewall policy: ```text input: REJECT output: ACCEPT forward: REJECT ``` Zones: ### `lan` ```text name='lan' input='ACCEPT' output='ACCEPT' forward='REJECT' masq='1' network='lan' ``` ### `wan` ```text name='wan' input='REJECT' output='ACCEPT' forward='REJECT' masq='1' mtu_fix='1' network='wan' 'wan6' 'wwan' 'usbwan' ``` ### `vpn` ```text name='vpn' input='ACCEPT' output='ACCEPT' forward='ACCEPT' masq='1' network='vpn' ``` Forwarding: ```text lan -> vpn ``` There is no general `lan -> wan` forwarding. Instead, selected `lan -> wan` traffic is allowed before a LAN-to-WAN killswitch. ## Important Firewall Rules ### Cloudflare Tunnel QUIC Direct Bypass Added 2026-07-14: ```text firewall.@rule[8].name='Allow-Mac-cloudflared-QUIC-direct' firewall.@rule[8].src='lan' firewall.@rule[8].dest='wan' firewall.@rule[8].src_ip='192.168.6.104' '192.168.6.173' firewall.@rule[8].proto='udp' firewall.@rule[8].dest_port='7844' firewall.@rule[8].target='ACCEPT' ``` The nft realization appears in `forward_lan` before the killswitch: ```text ip saddr { 192.168.6.104, 192.168.6.173 } udp dport 7844 jump accept_to_wan comment "!fw4: Allow-Mac-cloudflared-QUIC-direct" ``` At capture time, this rule had matched traffic: ```text counter packets 19 bytes 27636 ``` This confirms that Mac `cloudflared` QUIC traffic was hitting the direct-WAN exception. ### Upstream LAN Bypass (`Allow-upstream-LAN`) ```text firewall.@rule[10].name='Allow-upstream-LAN' firewall.@rule[10].src='lan' firewall.@rule[10].dest='wan' firewall.@rule[10].target='ACCEPT' firewall.@rule[10].dest_ip='192.168.2.0/24' '192.168.0.1' ``` ⚠️ **Порядковый номер изменился (2026-09-17).** Актуальное правило — `firewall.@rule[0].name='Allow-upstream-LAN'`, и `dest_ip` расширен до GPON-сегмента. Его nft-реализация в `forward_lan` — **две отдельные строки, `tcp` и `udp`, без `icmp`**: ```text meta l4proto tcp ip daddr { 192.168.0.1, 192.168.0.50, 192.168.2.0/24 } counter packets 19 bytes 1216 jump accept_to_wan comment "!fw4: Allow-upstream-LAN" meta l4proto udp ip daddr { 192.168.0.1, 192.168.0.50, 192.168.2.0/24 } counter packets 0 bytes 0 jump accept_to_wan comment "!fw4: Allow-upstream-LAN" ``` Практическое следствие: `forward` в GPON-сегмент разрешён, но без NAT ответ не вернётся, а ICMP вообще не проходит. Полный разбор — в разделе «🔴 Транзит Rasputin → 192.168.0.0/24» ниже. --- ## 🔴 ICMP к недоступному хосту в 192.168.2.0/24 отдаёт `Destination Port Unreachable` от САМОГО роутера **Наблюдение 2026-09-15 (подтверждено дважды в одной сессии).** С Mac (`192.168.6.104`) при пинге выключенного хоста верхнего сегмента: ```text $ /sbin/ping -c 3 192.168.2.176 92 bytes from rasputin.lan (192.168.6.1): Destination Port Unreachable Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 4 5 00 5400 a0a3 0 0000 3f 01 509d 192.168.6.104 192.168.2.176 --- 192.168.2.176 ping statistics --- 3 packets transmitted, 0 packets received, 100.0% packet loss ``` **Как это читать (и как НЕ читать):** | Признак | Значение | |---|---| | `Request timeout for icmp_seq N` | нормальный признак «хост выключен» | | `Destination Port Unreachable` от `rasputin.lan (192.168.6.1)` | **это роутер отбивает попытку, а не ответ хоста** | | Полный дамп IP-заголовка (`Vr HL TOS … Src/Dst`) | ответ сгенерирован ICMP-модулем роутера, а не удалённой машиной | | `100.0% packet loss` | хост не ответил | ```text /usr/sbin/arp -an | grep 176 ? (192.168.6.176) at (incomplete) on en8 ifscope [ethernet] ``` `(incomplete)` = ARP-запрос ушёл, ответа нет → **L2-ответа нет вообще**, хост мёртв или вне сети. Это правильный признак для вывода «машина недоступна». > ⚠️ **ПИТФОЛЛ ДИАГНОСТИКИ:** строка `Destination Port Unreachable` от роутера легко читается как «порт закрыт, машина жива». Это НЕВЕРНО. Она одинаково выглядит и для хоста, который выключен, и для хоста, который висит в kernel lockup без сетевого стека. Опираться нужно на **`arp (incomplete)` + 100% loss**, а не на текст ICMP-ошибки. > ⚠️ Проверять достижимость узлов `192.168.2.0/24` из `192.168.6.0/24` надёжнее всего **тем же каналом, которым потом будете работать**: SSH по ключу. `ssh -o ConnectTimeout=10 -o BatchMode=yes root@192.168.2.176 'uptime'` — если отвечает, хост жив, и всё дальнейшее снимается через него. Пинг/веб-порты могут врать (см. `Reject to LAN` ниже), SSH — нет. > 🔴 **Проверка 2026-09-17: 5 из 5 ICMP-попыток к `192.168.2.2` отбиты роутером** (`Destination Port Unreachable` от `192.168.6.1`), при том что с самого Rasputin `192.168.2.2` пингуется **0% loss, 0.75–0.84 ms**. То есть эхо-запрос из LAN до шлюза не доходит вообще — отбой происходит локально. Практический вывод: **отсутствие пинга к `192.168.2.x` НЕ говорит о том, что хост/шлюз недоступен.** Единственный вывод — «ICMP из LAN до этого адреса зарезан». Никогда не строить на пинге вывод о состоянии аплинка; проверять TCP-каналом, которым будете работать. > ⚠️ Ещё один контр-пример к пингу как индикатору (2026-09-17, `192.168.0.50`): с Мака — 100% loss **и без** `Destination Port Unreachable` (обычный timeout), а с Rasputin — 0% loss. Здесь пинг отражает реальную поломку policy routing, но **не** показывает, жив ли хост. Различить «хост мёртв» и «транзит сломан» можно одним дешёвым шагом: пингануть ту же цель **с самого Rasputin**. Роутер видит, клиент — нет ⇒ проблема в транзите, не в хосте. **Границы прав внутри сегментов (напоминание):** - Router transit `192.168.6.0/24 ↔ 192.168.2.0/24` — разрешён правилами выше. - Между сегментами есть санитарный `Reject to LAN` в `forward_lan` → произвольные порты не пройдут, ICMP-эхо тоже. - Порты HA (`8123`) и SSH (`22`) через транзит роутера **не проверять как индикатор живости** — индикатор это сам факт ответа SSH-сессии. ## 🔴 Транзит Rasputin → 192.168.0.0/24 (GPON-сегмент) НЕ РАБОТАЕТ **Наблюдение 2026-09-17.** Симптом: с Мака (LAN `192.168.6.0/24`) трафик на `192.168.0.50` и `192.168.0.1` (80 порт) не проходит. ### Что подтверждено фактами | Тест | Результат | |---|---| | `ping -c2 192.168.0.50` **с Мака** (`192.168.6.173`, en8) | 100% loss | | `ping -c2 192.168.0.50` **с самого Rasputin** | ✅ 0% loss, 2.11–2.79 ms, ttl=63 | | `ping -c2 192.168.2.2` с Мака | 100% loss + `Destination Port Unreachable` от `192.168.6.1` | Вывод из таблицы: **L2/L3 от Rasputin до `192.168.0.50` живой** (`ttl=63` — один хоп). Ломается **только транзит из LAN `192.168.6.0/24`**. ### Три причины **1. `ip rule 100` покрывает только `192.168.2.0/24`.** Фактические правила на роутере (2026-09-17): ```text 0: from all lookup local 98: from 192.168.6.173 ipproto udp dport 7844 lookup main 99: from 192.168.6.104 ipproto udp dport 7844 lookup main 100: from all to 192.168.2.0/24 lookup main 101: from all iif br-lan lookup vpn 32766: from all lookup main 32767: from all lookup default ``` Для `192.168.0.50` правило `100` **не матчится** → пакет попадает в `101` → `table vpn` → `tun0` (VLESS). Трафик уходит в туннель вместо WAN. **2. NAT/masq для зоны `wan` не покрывает `192.168.0.0/24`.** Правило firewall уже расширено до GPON-сегмента: ```text meta l4proto tcp ip daddr { 192.168.0.1, 192.168.0.50, 192.168.2.0/24 } counter packets 19 bytes 1216 jump accept_to_wan comment "!fw4: Allow-upstream-LAN" meta l4proto udp ip daddr { 192.168.0.1, 192.168.0.50, 192.168.2.0/24 } counter packets 0 bytes 0 jump accept_to_wan comment "!fw4: Allow-upstream-LAN" ``` То есть forward разрешён, но пакет уходит с source `192.168.6.x`, а GPON-сегмент не знает обратного маршрута в `192.168.6.0/24`. Ответ вернуться не может. В `forward_lan` **нет NAT-правила** для этого направления. **3. ICMP зарезан отдельно.** `Allow-upstream-LAN` в nft создан в двух вариантах — `tcp` и `udp`, **без `icmp`** — и стоит выше KillSwitch. Остальной ICMP из LAN падает в `KillSwitch` (`packets 71 bytes 6192`). Отсюда `Destination Port Unreachable` от самого роутера (см. раздел про ICMP ниже). > ⚠️ Из-за п.3 **пинг не является индикатором** доступности `192.168.0.0/24` из LAN. Проверять только тем каналом, которым будете работать (SSH/TCP). ### WAN-интерфейс изменился ДВАЖДЫ (2026-09-17 → 2026-09-22) **Текущее состояние (2026-09-22, проверено вживую):** ```text eth0: 192.168.1.50/24, MAC 36:ae:87:04:08:dc, link 1000/full br-lan: 192.168.6.1/24 default: via 192.168.1.1 dev eth0 proto static src 192.168.1.50 ``` 🔴 **Распутин теперь воткнут в сегмент `192.168.1.0/24`** — тот же, где Кра́кен (`.14`/`.15`), ТВ (`.70`) и Mac (`.57`/`.69`). Прежний `192.168.2.157/24` (gw `192.168.2.2`) **больше не активен**. Доступ: `ssh root@192.168.6.1` (LAN-адрес бранча, работает с Mac, хотя Mac в другом сегменте — через `192.168.1.50`). Историческая хронология WAN: | Дата | WAN-интерфейс | Адрес | GW | |---|---|---|---| | 2026-07-22 | `usb0` | `10.252.240.230` | `10.252.240.133` | | 2026-09-17 | `eth0` | `192.168.2.157/24` | `192.168.2.2` | | **2026-09-22** | **`eth0`** | **`192.168.1.50/24`** | **`192.168.1.1`** | Топология запроса (2026-09-17): `rasputin → 192.168.2.2 → 192.168.0.1 (GPON) → 192.168.0.50`. После переезда 2026-09-22 цепочка аплинка изменилась — `192.168.2.2` из пути выпал. ### Измерения Rasputin → Кра́кен (2026-09-22) ```text Rasputin → 192.168.1.14: 0.499 / 0.639 / 0.897 ms 10/10, 0% loss Rasputin → 192.168.1.15: 0.444 / 0.641 / 1.037 ms 10/10, 0% loss ``` ARP на Rasputin: и `.14`, и `.15` резолвятся в **один** lladdr `2c:cf:67:64:03:f3` (eth0 Кра́кена). ⚠️ На OpenWrt **`ping -i 0.3` не поддерживается** — busybox-ping отдаёт `ping: invalid number '0.3'`. Использовать `ping -c N` без `-i` или с целым числом. 🔴 **Пропускная способность не измерена** (2026-09-22). `iperf3` **установлен** на Распутине (`opkg install iperf3` → 3.17.1, `/usr/bin/iperf3`) и на Кра́кене (3.12, `apt-get install iperf3`). Замер не сделан, потому что **sshd на Распутине отказал** (`Connection refused` при живом пинге, см. ниже). ⚠️ На OpenWrt в комплекте уже были `libiperf3` (3.17.1-r4) и `python3-iperf3` — это **библиотеки, не бинарь**. `which iperf3` до установки пакета пусто. Проверять `which`, а не `opkg list-installed | grep iperf`. Замер, когда sshd вернётся: ```bash # на Кра́кене nohup iperf3 -s -p 5201 > /tmp/iperf3s.log 2>&1 & # на Распутине iperf3 -c 192.168.1.14 -p 5201 -t 10 ``` ⚠️ `1000FD` в `robocfg`/`ethtool` — это **link speed**, НЕ throughput. Реальные Мбит/с даёт только `iperf3`. Гигабит физически даёт максимум ~940–950 Мбит/с (оверход Ethernet); на NanoPi R5S потолок может быть ниже — упрётся в CPU. ## ⚠️ sshd на Распутине отказывает (2026-09-22, НЕ закрыто) Симптом: `ssh root@192.168.6.1` → `Connection refused`, при этом `ping 192.168.6.1` идёт 0% loss (4.4–7.6 ms), и `nc -z 192.168.6.1 22` → `CLOSED`. То есть **сеть до роутера жива, но sshd не слушает**. В начале той же сессии SSH работал. Не выяснено: упал ли sshd, кончились ли ресурсы, сработала ли защита, или дело в watchdog/перезагрузке. Диагностика на месте (когда будет доступ): ```sh service dropbear status # OpenWrt: dropbear, НЕ openssh logread | grep -i dropbear netstat -tlnp | grep :22 ``` ⚠️ Это **второй** случай нестабильного SSH за сессию: ASUS (`192.168.1.1`) тоже начал отдавать `Connection closed by 192.168.1.1 port 22` без причины (порт 22 при этом `nc -z` → succeeded). При недоступности роутера по SSH — сначала `ping` + `nc -z`, чтобы развести «сеть легла» и «демон отказал». ### ✅ ЧТО СДЕЛАНО (2026-09-17) — правило `ip rule` добавлено и работает Применено и **проверено Alex руками** (80 порт на `192.168.0.50` открылся): ```sh # на живом роутере ip rule add to 192.168.0.0/24 lookup main priority 100 # и то же записано в /etc/rc.local (чтобы выжило ребут) ``` Бэкап до правки: `/etc/rc.local.bak.20260917-132639` Правила после правки (две строки priority 100 — это нормально): ```text 100: from all to 192.168.2.0/24 lookup main 100: from all to 192.168.0.0/24 lookup main 101: from all iif br-lan lookup vpn ``` > 🔴 **Правка файлов на роутере — через скачивание → локальную правку → заливку.** > `sed -i` по живому конфигу **запрещён** (и был заблокирован в этой сессии). > Порядок: `scp` вниз → `patch` локально → `scp` наверх → `cat` для проверки. ### ⚠️ Что ОСТАЛОСЬ не сделано (осознанно, по объёму команды «ip rule») Alex дал команду только на `ip rule`. Следующие два пункта **не трогались**: 1. **NAT/masq для `192.168.0.0/24`** — нет. Пакет уходит с source `192.168.6.x`. Проверка показала, что 80 порт всё равно отвечает (GPON, судя по всему, маршрутизирует обратно), поэтому это **не блокер**, но и не гарантия. 2. **ICMP** — `Allow-upstream-LAN` в nft по-прежнему только `tcp`+`udp`, без `icmp`. Значит **пинг из LAN в `192.168.0.0/24` не работает до сих пор**, хотя TCP/UDP проходит. Не путать «не пингуется» с «не работает». ### План ремонта (исходный, для истории) ```sh # 1. ip rule — расширить bypass на GPON-сегмент ← СДЕЛАНО ip rule add to 192.168.0.0/24 lookup main priority 100 # 2. та же строка в /etc/rc.local ← СДЕЛАНО # 3. firewall: Allow-upstream-LAN dest_ip += 192.168.0.0/24, добавить proto icmp ← НЕ сделано # 4. проверить, что masq зоны wan покрывает 192.168.0.0/24 ← НЕ сделано ``` Порядок работ по боевому конфигу: коммит ДО → правка → заливка → проверка Алекса руками → коммит ПОСЛЕ. ## Downstream LAN Access ```text firewall.@rule[11].name='Allow-downstream-LAN' firewall.@rule[11].src='wan' firewall.@rule[11].dest='lan' firewall.@rule[11].target='ACCEPT' firewall.@rule[11].src_ip='192.168.2.0/24' '192.168.0.1' ``` ### KillSwitch ```text firewall.@rule[12].name='KillSwitch' firewall.@rule[12].src='lan' firewall.@rule[12].dest='wan' firewall.@rule[12].proto='all' firewall.@rule[12].target='REJECT' ``` In nft `forward_lan`, ordering is important: ```text 1. Allow-Mac-cloudflared-QUIC-direct 2. Allow-upstream-LAN 3. KillSwitch 4. Accept lan to vpn forwarding 5. Accept DNAT port forwards 6. Reject to LAN ``` This ordering means only explicit `lan -> wan` exceptions escape; everything else either goes through VPN routing or is rejected from WAN. Backup made before the Cloudflare firewall change: ```text /etc/config/firewall.bak.20260714-195530 ``` ## Port Forwarding There is a port-forward named `LocalSend-to-mbp-black-eth`: ```text firewall.@redirect[0].name='LocalSend-to-mbp-black-eth' firewall.@redirect[0].src='wan' firewall.@redirect[0].dest='lan' firewall.@redirect[0].src_ip='192.168.2.0/24' firewall.@redirect[0].proto='tcp udp' firewall.@redirect[0].src_dport='53317' firewall.@redirect[0].dest_ip='192.168.6.104' firewall.@redirect[0].dest_port='53317' firewall.@redirect[0].target='DNAT' ``` Reflection rules exist for LAN clients hitting the router WAN IP on port `53317`, redirecting to `192.168.6.104:53317`. ## Cloudflare Tunnel / Mac Bypass Context Problem addressed: - Mac runs `cloudflared` for `zulip.qentra.top`. - Router routes normal LAN traffic through VLESS via `tun0`. - Cloudflare Tunnel connector traffic should go direct to WAN rather than through VLESS. Chosen solution: 1. Mac `cloudflared` should use QUIC, not HTTP/2. 2. Cloudflare Tunnel QUIC is UDP destination port `7844`. 3. Router sends only Mac UDP/7844 through `main` table. 4. Firewall allows only Mac UDP/7844 from LAN to WAN before the KillSwitch. Mac-side LaunchDaemon observed earlier: ```text /Library/LaunchDaemons/com.cloudflare.cloudflared.plist ``` Original args observed before the change: ```text cloudflared tunnel --protocol http2 run --token ``` Target Mac-side args: ```text cloudflared tunnel --protocol quic run --token ``` Manual Mac commands provided for that change: ```sh sudo cp /Library/LaunchDaemons/com.cloudflare.cloudflared.plist \ /Library/LaunchDaemons/com.cloudflare.cloudflared.plist.bak.$(date +%Y%m%d-%H%M%S) sudo /usr/libexec/PlistBuddy \ -c "Set :ProgramArguments:3 quic" \ /Library/LaunchDaemons/com.cloudflare.cloudflared.plist sudo launchctl bootout system /Library/LaunchDaemons/com.cloudflare.cloudflared.plist sudo launchctl bootstrap system /Library/LaunchDaemons/com.cloudflare.cloudflared.plist ``` Verify on Mac: ```sh plutil -p /Library/LaunchDaemons/com.cloudflare.cloudflared.plist tail -50 /Library/Logs/com.cloudflare.cloudflared.err.log ``` Expected log clue: ```text protocol=quic ``` Router-side counters can verify traffic is hitting the bypass: ```sh nft list chain inet fw4 forward_lan ``` Look for increasing counters on: ```text Allow-Mac-cloudflared-QUIC-direct ``` ## Current Effective Traffic Behavior ```text Mac 192.168.6.104 UDP/7844 -> main routing table -> WAN direct Mac 192.168.6.173 UDP/7844 -> main routing table -> WAN direct Any LAN client -> 192.168.2.0/24 -> main routing table -> WAN/upstream LAN LAN DNS to Rasputin:53 -> clientdns -> SOCKS/Xray -> TrueNAS -> Kraken Other LAN TCP traffic -> vpn routing table -> tun0 -> Xray/VLESS General client UDP via tun2socks -> not configured LAN -> WAN not explicitly allowed -> rejected by KillSwitch ``` ## Operational Commands Restart VPN tunnel policy: ```sh /etc/init.d/tunvpn restart ``` Restart firewall: ```sh /etc/init.d/firewall restart ``` Show policy routing: ```sh ip rule show ip route show table vpn ip route show table all ``` Show Cloudflare direct firewall counter: ```sh nft list chain inet fw4 forward_lan ``` Show Xray logs: ```sh tail -100 /tmp/xray-access.log tail -100 /tmp/xray-error.log ``` Show active proxy processes: ```sh ps w | grep -Ei 'xray|badvpn|tun2socks' | grep -v grep ``` Show current DHCP static leases: ```sh uci show dhcp | grep -E 'mbp-black|192.168.6.104|192.168.6.173|00:e0:4c:68:04:88|66:02:b1:8b:2f:94' ``` ## Recovery / Rollback DHCP backup from Mac static lease change: ```text /etc/config/dhcp.bak.20260714-195408 ``` Routing service backup from Cloudflare direct-bypass change: ```text /etc/init.d/tunvpn.bak.20260714-195530 ``` Firewall backup from Cloudflare direct-bypass change: ```text /etc/config/firewall.bak.20260714-195530 ``` Роутинг через туннель (`tunvpn`) — 2026-07-14. Firewall/Cloudflare bypass — 2026-07-14. GPON-транзит (`ip rule` на `192.168.0.0/24`) — 2026-09-17. Rollback example: ```sh cp /etc/init.d/tunvpn.bak.20260714-195530 /etc/init.d/tunvpn chmod +x /etc/init.d/tunvpn cp /etc/config/firewall.bak.20260714-195530 /etc/config/firewall /etc/init.d/tunvpn restart /etc/init.d/firewall restart ``` Rollback DHCP static leases: ```sh cp /etc/config/dhcp.bak.20260714-195408 /etc/config/dhcp uci commit dhcp /etc/init.d/dnsmasq restart ``` Rollback GPON-транзита (2026-09-17): ```sh # живая система ip rule del to 192.168.0.0/24 lookup main priority 100 # постоянство cp /etc/rc.local.bak.20260917-132639 /etc/rc.local ``` > ⚠️ Правки `/etc/rc.local` на роутере делать **только** через `scp`-вниз → `patch` локально → > `scp`-наверх. `sed -i` по живому конфигу запрещён. ## Notes And Pitfalls - The Cloudflare bypass depends on Mac `cloudflared` using QUIC. If the Mac falls back to HTTP/2/TCP/443, the router exception will not match. - There are two Mac source IPs because the Mac can use Ethernet or Wi-Fi. - Both Mac IPs are now static DHCP reservations on Rasputin. - `ip rule` can match `ipproto udp dport 7844` on this OpenWrt build; this was tested and confirmed before making the persistent change. - The firewall allow alone would not be sufficient, because policy routing rule `101` sends LAN ingress to `table vpn`. The direct `ip rule`s at priorities `98` and `99` must remain before priority `101`. - Rule order in `forward_lan` matters: Cloudflare direct allow must stay above `KillSwitch`. - `eth0` is the **active** WAN as of 2026-09-17 (`192.168.2.157/24`, gw `192.168.2.2`); `usb0` is idle. At the 2026-07-22 capture this was inverted (`eth0` down, `usb0` carrying the default route). Do not trust the old banner — re-read `ip route` before assuming which WAN is live. - 🔴 **Транзит из LAN `192.168.6.0/24` в GPON-сегмент `192.168.0.0/24` — ЧАСТИЧНО ПОЧИНЕН (2026-09-17).** `ip rule` добавлено (живая система + `/etc/rc.local`), Alex проверил 80 порт на `192.168.0.50` — работает. Было три причины: `ip rule 100` не покрывал `192.168.0.0/24` (✅ исправлено); нет NAT/masq для этого направления (❌ не сделано, не блокер); ICMP не разрешён в `Allow-upstream-LAN` (❌ не сделано). Подробности — в разделе «Транзит Rasputin → 192.168.0.0/24». - 🔴 **ICMP по-прежнему не индикатор доступности `192.168.0.0/24`.** `Allow-upstream-LAN` создан только для `tcp`+`udp`, поэтому эхо-запросы из LAN падают в `KillSwitch` и роутер отвечает `Destination Port Unreachable` от себя. **Пинг в GPON-сегмент не работает даже после починки `ip rule`** — TCP/UDP при этом проходит. Проверять TCP-портом (`nc -zv 192.168.0.50 80`) или SSH. - Различай два похожих симптома: `Destination Port Unreachable` **от роутера `192.168.6.1`** = локальное отбивание ICMP/firewall-ом, а не закрытый порт на хосте. Признак живости — ответ на TCP/SSH, а не текст ICMP-ошибки. - 🔴 **Ping from Mac to an unreachable `192.168.2.x` host returns `Destination Port Unreachable` from `rasputin.lan (192.168.6.1)`, NOT a plain timeout.** This is the router rejecting the attempt — it does **not** mean the host is up with a closed port. Use `arp (incomplete)` + `100% packet loss` as the liveness signal. Confirmed 2026-09-15 (see the dedicated section above). - Liveness of `192.168.2.0/24` hosts is best verified with the channel you intend to work through: key-based SSH (`ssh -o BatchMode=yes root@192.168.2.176 'uptime'`). Ping and web ports can mislead; an answered SSH session cannot. - **Дешёвый способ развести «хост мёртв» и «транзит сломан»:** пингануть цель *с самого Rasputin* (`ssh root@192.168.6.1 'ping -c2 '`). Если роутер видит хост, а LAN-клиент — нет, проблема в policy routing/firewall, а не в хосте. Именно так 2026-09-17 локализовали GPON-проблему. - Secrets are redacted here intentionally. Consult the live router files only when the actual VLESS or Cloudflare credentials are needed.