Files
obsidian-vault/family/how-to/rasputin-router.md
T

47 KiB
Executable File
Raw Blame History

title, created, updated, tags
title created updated tags
Rasputin OpenWrt router 2026-07-22 2026-10-30
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

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.

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:

default dev tun0 scope link

Interfaces

LAN

br-lan bridges:

eth1
eth2
phy0-ap0

LAN config:

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:

network.wan=interface
network.wan.device='eth0'
network.wan.proto='dhcp'
network.wan.peerdns='0'
network.wan.type='bridge'

usb0:

network.usbwan=interface
network.usbwan.proto='dhcp'
network.usbwan.device='usb0'

Firewall wan zone includes:

wan
wan6
wwan
usbwan

VPN

tun0 is declared as an unmanaged interface:

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:

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:

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:

/etc/config/dhcp.bak.20260714-195408

Клиентский DNS через туннель

DHCP выдаёт клиентам 192.168.6.1:

dhcp.lan.dhcp_option='6,192.168.6.1'
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:

# 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:

{
  "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:

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:

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

На роутере:

/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.

Проверка обработчика на роутере и отдельной маршрутизации локальных имён:

nslookup www.youtube.com 127.0.0.1:1053
nslookup rasputin.lan 127.0.0.1:1053

Проверка с LAN-клиента через стандартный DNS-порт:

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, а не клиентский путь.

Логи отдельной службы:

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:

/etc/init.d/clientdns restart

Отключение и откат clientdns

Подключиться по IP: ssh root@192.168.6.1. На роутере выполнить:

sh /etc/clientdns/rollback.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:

badvpn
xray-core
v2ray-core
v2ray-extra
v2ray-geoip
v2ray-geosite
v2raya
luci-app-v2raya
sing-box
nftables-json
iptables-nft

Runtime processes:

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. Автозапуск включён.

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 в документации скрыт; брать его из действующего конфига.

{
  "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": "<VLESS_UUID>",
                "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.:

/usr/bin/xray run -test -config /etc/xray/config.json < /dev/null

Перезапустить только основную службу Xray:

/etc/init.d/xray restart
/etc/init.d/xray status

Проверить автозапуск и состояние процесса под procd:

/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 должен слушаться.

Логи:

tail -100 /tmp/xray-error.log
tail -100 /tmp/xray-access.log
logread -e xray

Файлы в /tmp очищаются при перезагрузке. В access-log [socks-in >> proxy] означает передачу запроса в основной VLESS-выход.

Сквозная проверка основного прокси с удалённым разрешением имени:

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:

/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:

#!/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:

/etc/init.d/tunvpn.bak.20260714-195530

Policy Routing

Current rules (снято 2026-09-22, живая система):

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:

# 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:

input:   REJECT
output:  ACCEPT
forward: REJECT

Zones:

lan

name='lan'
input='ACCEPT'
output='ACCEPT'
forward='REJECT'
masq='1'
network='lan'

wan

name='wan'
input='REJECT'
output='ACCEPT'
forward='REJECT'
masq='1'
mtu_fix='1'
network='wan' 'wan6' 'wwan' 'usbwan'

vpn

name='vpn'
input='ACCEPT'
output='ACCEPT'
forward='ACCEPT'
masq='1'
network='vpn'

Forwarding:

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:

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:

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:

counter packets 19 bytes 27636

This confirms that Mac cloudflared QUIC traffic was hitting the direct-WAN exception.

Upstream LAN Bypass (Allow-upstream-LAN)

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:

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) при пинге выключенного хоста верхнего сегмента:

$ /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 хост не ответил
/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):

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-сегмента:

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, проверено вживую):

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)

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 вернётся:

# на Кра́кене
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/перезагрузке.

Диагностика на месте (когда будет доступ):

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 открылся):

# на живом роутере
ip rule add to 192.168.0.0/24 lookup main priority 100

# и то же записано в /etc/rc.local (чтобы выжило ребут)

Бэкап до правки: /etc/rc.local.bak.20260917-132639

Правила после правки (две строки priority 100 — это нормально):

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 проходит. Не путать «не пингуется» с «не работает».

План ремонта (исходный, для истории)

# 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

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

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:

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:

/etc/config/firewall.bak.20260714-195530

Port Forwarding

There is a port-forward named LocalSend-to-mbp-black-eth:

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:

/Library/LaunchDaemons/com.cloudflare.cloudflared.plist

Original args observed before the change:

cloudflared tunnel --protocol http2 run --token <redacted-cloudflare-tunnel-token>

Target Mac-side args:

cloudflared tunnel --protocol quic run --token <redacted-cloudflare-tunnel-token>

Manual Mac commands provided for that change:

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:

plutil -p /Library/LaunchDaemons/com.cloudflare.cloudflared.plist
tail -50 /Library/Logs/com.cloudflare.cloudflared.err.log

Expected log clue:

protocol=quic

Router-side counters can verify traffic is hitting the bypass:

nft list chain inet fw4 forward_lan

Look for increasing counters on:

Allow-Mac-cloudflared-QUIC-direct

Current Effective Traffic Behavior

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:

/etc/init.d/tunvpn restart

Restart firewall:

/etc/init.d/firewall restart

Show policy routing:

ip rule show
ip route show table vpn
ip route show table all

Show Cloudflare direct firewall counter:

nft list chain inet fw4 forward_lan

Show Xray logs:

tail -100 /tmp/xray-access.log
tail -100 /tmp/xray-error.log

Show active proxy processes:

ps w | grep -Ei 'xray|badvpn|tun2socks' | grep -v grep

Show current DHCP static leases:

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:

/etc/config/dhcp.bak.20260714-195408

Routing service backup from Cloudflare direct-bypass change:

/etc/init.d/tunvpn.bak.20260714-195530

Firewall backup from Cloudflare direct-bypass change:

/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:

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:

cp /etc/config/dhcp.bak.20260714-195408 /etc/config/dhcp
uci commit dhcp
/etc/init.d/dnsmasq restart

Rollback GPON-транзита (2026-09-17):

# живая система
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 rules 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 <target>'). Если роутер видит хост, а 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.