852 lines
31 KiB
Markdown
852 lines
31 KiB
Markdown
---
|
||
title: Rasputin OpenWrt router
|
||
created: '2026-07-22'
|
||
updated: '2026-09-17'
|
||
tags:
|
||
- openwrt
|
||
- router
|
||
- rasputin
|
||
- networking
|
||
- vless
|
||
- cloudflared
|
||
---
|
||
# Rasputin R5S OpenWrt router
|
||
|
||
> Status as of 2026-09-17: Rasputin is the OpenWrt edge/LAN router, routing normal LAN traffic through a VLESS/Xray tunnel via `tun0`, with explicit direct-WAN exceptions for upstream LAN access and Mac `cloudflared` QUIC traffic. ⚠️ **WAN переехал на `eth0` (`192.168.2.157/24`, gw `192.168.2.2`)** — см. раздел «WAN-интерфейс изменился» ниже. Транзит в GPON-сегмент `192.168.0.0/24` **сломан** — см. одноимённый раздел.
|
||
|
||
## 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 devices are `eth0` and `usb0` in the `wan` firewall zone. **As of 2026-09-17 `eth0` is the active WAN** (it carries the default route); `usb0` is idle. At the 2026-07-22 capture this was inverted — `eth0` had no carrier and `usb0` held the default route.
|
||
|
||
Default route at the 2026-07-22 capture (historical, no longer active):
|
||
|
||
```text
|
||
default via 10.252.240.133 dev usb0 src 10.252.240.230
|
||
```
|
||
|
||
Current default route (2026-09-17):
|
||
|
||
```text
|
||
default via 192.168.2.2 dev eth0 proto static src 192.168.2.157
|
||
```
|
||
|
||
That is, Rasputin gets `192.168.2.157/24` from the upstream router `192.168.2.2` and reaches the internet through it; `192.168.2.2` in turn sits behind GPON `192.168.0.1`.
|
||
|
||
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,1.1.1.1,1.0.0.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
|
||
```
|
||
|
||
## 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 -confdir /etc/xray -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
|
||
|
||
File:
|
||
|
||
```text
|
||
/etc/xray/config.json
|
||
```
|
||
|
||
Structure:
|
||
|
||
```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": "188.239.191.235",
|
||
"port": 443,
|
||
"users": [
|
||
{
|
||
"id": "<redacted-vless-user-id>",
|
||
"flow": "xtls-rprx-vision",
|
||
"encryption": "none"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
"streamSettings": {
|
||
"network": "tcp",
|
||
"security": "reality",
|
||
"realitySettings": {
|
||
"serverName": "node3.sysnx.net",
|
||
"publicKey": "<redacted-reality-public-key>",
|
||
"shortId": "<redacted-reality-short-id>",
|
||
"fingerprint": "qq"
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"routing": {
|
||
"rules": [
|
||
{ "type": "field", "outboundTag": "proxy", "network": "tcp,udp" }
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
Secrets are intentionally redacted in this note. The live file on the router contains the actual VLESS UUID and REALITY parameters.
|
||
|
||
## `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:
|
||
|
||
```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
|
||
```
|
||
|
||
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 the GPON segment `192.168.0.0/24` is **NOT** covered by rule `100` (2026-09-17) → falls through to rule `101` → `table vpn` → `tun0`. Доступ к GPON-сегменту из LAN `192.168.6.0/24` не работает. See the «🔴 Транзит Rasputin → 192.168.0.0/24» section.
|
||
- 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)
|
||
|
||
Ранее (capture 2026-07-22) WAN был `usb0` с адресом `10.252.240.230`. Сейчас **WAN = `eth0`**:
|
||
|
||
```text
|
||
inet 192.168.2.157/24 scope global eth0
|
||
default via 192.168.2.2 dev eth0 proto static src 192.168.2.157
|
||
```
|
||
|
||
То есть Rasputin получает адрес `192.168.2.157` **от роутера `192.168.2.2`** и выходит в интернет через него, а `192.168.2.2` — через GPON `192.168.0.1`. Топология запроса подтверждена: `rasputin → 192.168.2.2 → 192.168.0.1 (GPON) → 192.168.0.50`.
|
||
|
||
### ✅ ЧТО СДЕЛАНО (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 <redacted-cloudflare-tunnel-token>
|
||
```
|
||
|
||
Target Mac-side args:
|
||
|
||
```text
|
||
cloudflared tunnel --protocol quic run --token <redacted-cloudflare-tunnel-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
|
||
Other LAN traffic -> vpn routing table -> tun0 -> Xray/VLESS
|
||
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 <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.
|