On this page
Home Network Architecture Documentation
TL;DR: Telecom GPON bridged → Xiaomi main router PPPoE dial-up → Whole-house wired Mesh (4 APs) → N100 single-port side router (mihomo + AdGuard + CN-IP hard bypass) → li-home-0 directly connected to main router + rathole for external exposure. Default traffic for the whole house goes through the proxy; core desktop traffic goes direct.
Topology version v2.1, this document serves as the single source of truth for whole-house network operations.
1. Physical Topology
The optical modem only performs optical-electrical conversion (bridged mode, no dial-up, no routing, no NAT). The public IP goes directly to the main router's PPPoE interface, avoiding double NAT. The house is approximately 120m² with four rooms. A single AP cannot cover all rooms (especially the master and secondary bedrooms, which are separated by two load-bearing walls). The 4 AP wired Mesh covers dead zones + provides unified SSID seamless roaming. All APs use wired backhaul, not consuming wireless bandwidth.
2. Mesh Network
| Node | IP | Hardware | Location | Coverage Challenge | Backhaul |
|---|---|---|---|---|---|
| root | .1 | RP01 | Entrance | Living room center | — |
| leaf | .62 | RC06 | Living room | Covers entrance area | Wired |
| leaf | .67 | RP03 | Master bedroom | Separated by two load-bearing walls | Wired |
| leaf | .80 | RP03 | Study | Core equipment area | Wired |
| leaf | .227 | RP03 | Secondary bedroom | Furthest, no signal from single AP | Wired |
SSID: 308_6G_WIFI (5G, daily use) + Hidden (2.4G, IoT dedicated).
3. Key Hosts
| Device | IP | Role | Connection | OS |
|---|---|---|---|---|
| ONT | — (Bridged, no IP) | Optical-electrical conversion | Fiber in, Ethernet out | Telecom GPON |
| Xiaomi Main Router | .1 | PPPoE/NAT/DHCP/Mesh root | WAN connected to modem | MiWiFi (RP01) |
| li-home-router | .7 | Side router: Transparent proxy + DNS (AdGuard) | Wired single port, hangs on Study AP LAN | iStoreOS (N100) |
| li-home-0 | .8 | Desktop: Dev + External services, Static IP Gateway→.1, no proxy | WiFi (MT7927), connects to Study AP | Gentoo |
| li-home-1 | .9 | Retired side router, SSH only | WiFi | Win11 |
Key Design: li-home-0 (.8) is statically configured with its gateway pointing to the main router (.1). The side router's transparent proxy does not cover it—this machine runs its own TUN mode mihomo for self-proxying. Its DNS uses real IP architecture, and it follows a different proxy path than other devices in the house.
4. DHCP & DNS
- Gateway: DHCP defaults to issuing .7 (N100), so all house traffic goes through the side router proxy. Exception: li-home-0 (.8) is statically configured with gateway→.1.
- DNS: Primary .7 (N100 AdGuard), with public DNS backup as fallback.
- Lease: 1–2 hours. Shortening this accelerates DHCP recovery in case of main router failure.
- Address Pool: 192.168.31.5–250.
Normal DNS Path
5. Traffic Paths
Normal Mode
① Mainland IPs are returned at the firewall layer, physically not entering mihomo — Hard protection for domestic traffic. The single-port hairpin (in and out via the same br-lan) forwards back to .1, must MASQUERADE the source to .7, otherwise asymmetric return traffic will cause immediate disconnection. ② fake-ip misses CN domains in geosite (getting 198.18.x) still enter mihomo, relying on the tail rule
GEOIP,CN,DIRECT(resolving real IP) as a secondary fallback. li-home-0 uses TUN self-proxying separately, DNS returns real IPs, no fake-ip ambiguity, and domestic connection is inherently stable.
Fail-Open Mode (mihomo/AdGuard Down)
6. Split-Route Strategy & Domestic Traffic Protection
A CN domain missed by geosite once caused MATCH,PROXY → US node reconnecting to domestic server → GFW identified "domestic traffic" and blocked the VPS IP. Now using Dual-Layer Protection:
Layer 1 (Hard, Firewall): bypass_china_mainland_ip=1 (v4) populates the nft china_ip set (~3.7k chnroute). Mainland IPs are returned at prerouting, physically unable to reach mihomo, regardless of whether the domain matches geosite.
- Single-port hairpin requires
table inet bypass_masqto MASQUERADE forwarded traffic source to .7. - v6 firewall bypass is currently disabled (to avoid v6 hairpin issues); v6 CN traffic falls back to mihomo
GEOIP,CN.
Layer 2 (Fallback, mihomo Rules): Tail rules GEOSITE,cn,DIRECT → GEOIP,CN,DIRECT → MATCH,PROXY. CN domains missed by fake-ip enter mihomo, where GEOIP,CN resolves the real IP to intercept.
Key sysctl
| Parameter | Value | Reason |
|---|---|---|
net.ipv4.conf.*.send_redirects | 0 | Otherwise .7 sends ICMP redirects, causing clients to bypass .7 and connect directly to .1, forming a "dual path": same target client direct connection + mihomo dial-up coexist. After NAT through the same public IP on the main router, the server PAWS discards the one with the smaller timestamp → i/o timeout storm (the real culprit for apps like Xianyu freezing). Applies to all single-arm/hairpin side router solutions, not just this one. |
net.ipv4.conf.*.rp_filter | 0 | Allows asymmetry, works with hairpin |
International Lines
Three VPSs are distributed across different network lines. Latency and stability differences stem from the QoS levels of the return lines:
| Line | Full Name | Priority | Characteristics | Suitable For |
|---|---|---|---|---|
| CN2 GIA | China Telecom Next Gen Carrier | Highest | Telecom top-tier line, entire CN2 backbone without passing through 163 network, lowest QoS, basically no speed drop during evening peaks | Main nodes requiring low latency/stability |
| 9929 | China Unicom Premium | Medium-High | Unicom premium line, similar to CN2 GIA but uses Unicom backbone, optimal for Unicom broadband users in the North | Optimal for Unicom users |
| 4837 | China Unicom Standard | Standard | Unicom standard international line, obvious congestion during evening peaks but cheap | Backup/Cold backup/Budget-sensitive |
Local Nodes
| Node | Line | Protocol | Latency | Notes |
|---|---|---|---|---|
| US3-Hy2 (Primary) | CN2 GIA | Hysteria2 (UDP+Brutal) | 136ms | Main node, stable during evening peaks; anti-packet loss |
| US2-Hy2 | 9929 | Hysteria2 | 168ms | Unicom line, fast in the North |
| US1-Hy2 | 4837 | Hysteria2 | 213ms | Cheap backup, slow during evening peaks |
| US*/REALITY | Same | VLESS+REALITY (TCP) | 420–569ms | Over GFW, QoS/packet loss amplifies latency, only for extreme backup |
Hy2 is UDP+Brutal for anti-packet loss, REALITY is TCP — when bypassing GFW, TCP is dropped by QoS and retransmissions amplify latency, while UDP bypasses directly. Thus, the three REALITY nodes have 3x the latency of Hy2 and are basically decorative.
PROXYis a manual Selector pinned to US3-Hy2, not Auto — better to manually switch if the primary fails than let mihomo auto-switch to a slow node (switching makes the user experience "internet is broken but not completely" — much harder to troubleshoot).
VPS Provider Reference
Line quality is important, but VPS providers matter too — for the same CN2 GIA, different vendors have vastly different oversubscription ratios, line quality, and TCP handshake packet loss rates to mainland China. Personal阶段性 research results (prices vary over time, for reference only):
High Quality (Primary Recommended):
| Vendor | Line | Reference Price | Characteristics |
|---|---|---|---|
| DMIT | CN2 GIA | ~$12–15/mo (1C1G) | Old CN2 GIA provider, extremely stable line, almost 0 packet loss to mainland; expensive but worth it |
| BandwagonHost (Old Dig) | CN2 GIA | ~$50–100/yr (CN2 GIA plan) | Old brand, CN2 GIA plan has good cost-performance, DIA line; often out of stock, need to wait |
| Sharon | CN2 GIA/9929 | ~$8–12/mo | Niche but solid lines, 9929 plan has outstanding cost-performance |
Cost-Effective (Usable):
| Vendor | Line | Reference Price | Characteristics |
|---|---|---|---|
| Lisa Host | 4837/CN2 Mix | ~$5–8/mo | Average lines (mostly 4837), slow during evening peaks, sufficient for backup lines not sensitive to latency |
| RackNerd | Standard International | ~$15–30/yr | Super cheap but poor return line, only suitable for cold backup/non-real-time use |
Cheap (Backup/Experiment):
| Vendor | Line | Reference Price | Characteristics |
|---|---|---|---|
| Yecao Cloud | 4837 | ~$3–5/mo | Cheap, severe congestion during evening peaks, only suitable as the last fallback |
| CloudCone | Standard International | ~$20–30/yr | Ultra-low annual payment, good OOKLA speeds but poor mainland TCP, only for cold backup |
Selection Principle: Don't save money on primary nodes — use DMIT or BandwagonHost for CN2 GIA, stability is speed; use 9929 (good for Unicom) or 4837 (cheap) for backups, hang REALITY cold backup on the cheapest one. Local US3 is on DMIT CN2 GIA, US2 on 9929, US1 on 4837 cheap machine.
Speedup Items
tcp-concurrent: true: Concurrent dial-up resolves multiple IPs and picks the fastest.- QUIC reject rule intentionally kept:
AND,(DST-PORT,443),(NETWORK,UDP),(NOT,GEOSITE,cn),REJECTforces external HTTP/3→h2, curing "QUIC-over-Hy2 idle reuse hang". Opening it will cause recurrence.
Why not hard bypass for IPv6: The key to v4 hard bypass is hairpin MASQUERADE (changing source to .7 to ensure symmetric return). This operation relies on N100 having a fixed internal v4 address. However, v6 addresses are dynamically assigned via SLAAC, with no stable internal address to act as an SNAT anchor — hairpin return symmetry cannot be reliably achieved on v6. Forcing it on will only cause random disconnections. Therefore, v6 CN currently only relies on mihomo rule layer
GEOIP,CNfallback, not firewall prerouting hard bypass. Monitoring: Key components of N100, li-home-0, and main router are integrated into Prometheus + Grafana, see monitoring.md. Node availability monitoring relies on Prometheus Blackbox Exporter probes, but switching is still manual (not using Auto is an intentional choice — better to manually switch if the primary fails than auto-switch to a slow node).
7. N100 Key Components
| Component | Address/Path | Description |
|---|---|---|
| mihomo Kernel | /usr/bin/mihomo, procd via /etc/init.d/nikki | Transparent Proxy + TUN |
| mihomo API | 127.0.0.1:9090 | Health Probe |
| mihomo Mixed | :7890 (SOCKS5+HTTP) | Explicit Proxy Entry |
| mihomo Redir | :7891 | Transparent Proxy Redirect Inbound |
| mihomo DNS | :1053 (fake-ip) | AdGuard Upstream |
| Nikki Dashboard | http://.7:9090/ui | Web Console |
| AdGuard Home | Docker host network, http://.7:8083 | DNS Ad Filtering, upstream→:1053 |
| failopen Daemon | /usr/local/sbin/nikki-failopen.sh, procd | Health Check + Auto Fail-Open |
| CN Bypass SNAT | /etc/nikki/scripts/bypass-masq.{nft,sh} + firewall include | Hairpin masquerade, persistent |
N100 Migration Motivation & Hardware Selection: Previously, the side router ran on the desktop via WiFi, and WiFi jitter caused whole-house disconnection. Switched to N100 (low-power x86, iStoreOS, single Gigabit port) hanging on the Study AP LAN port as a wired side router, decoupling from WiFi.
8. nftables Structure
Processing Order (prerouting → forward → postrouting):
table inet fw4 (OpenWrt Firewall)
forward: policy drop; allow TUN traffic + LAN→WAN forwardingsrcnat_lan: docker MASQUERADE
table inet nikki (Transparent Proxy Rules)
china_ipset: ~3.7k chnroute CIDRs (populated bybypass_china_mainland_ip=1)dstnat/mangle_prerouting_lan: Mainland IP / Private / 198.18 match →returnbypass; others DNAT TCP→:7891 or Mark TUNmangle_output: Local outbound → TUN- Entire table deleted on fail-open
table inet bypass_masq (CN-IP Bypass Hairpin SNAT)
postrouting(nat hook, priority srcnat+5):iifname br-lan oifname br-lan ip daddr != {private+198.18+multicast} masquerade- Only hits public CN direct traffic bypassed by china_ip that needs forwarding back to .1, changing source to .7 to ensure symmetric return
- Independent of table nikki, nikki restart does not affect; persistence relies on
firewall.bypass_masqinclude
table inet failopen (Failure Injection, Deleted on Recovery)
srcnat: MASQUERADE 192.168.31.0/24 → non-localdns: DNAT :53 → .1:53
9. External Services (li-home-0)
All these services rely on li-home-0's WiFi. wifi-watchdog (systemd timer 30s) automatically recovers from RF/driver anomalies — brief jitter can self-heal. Long-term high-availability services should be migrated to the wired N100.
| Service | Purpose | Availability |
|---|---|---|
| DERP | Tailscale Relay | High |
| rathole | Intranet Penetration (see rathole-tunnel.md) | High |
| paste/mdserve | Note Sharing | Medium |
| Prometheus | Monitoring Collection | Medium |
| sunshine | Game Streaming | Low |
| Local LLM | AI Inference (see local-llm.md) | Low |
10. Daemons
| Daemon | Location | Scenario |
|---|---|---|
| nikki-failopen | N100 (procd) | mihomo/AdGuard process abnormal |
| Power-on Self-Start | N100 (BIOS) | Power outage recovery |
| wifi-watchdog | li-home-0 (systemd timer) | WiFi driver/RF abnormal |
| sync-mihomo-to-router | li-home-0 (systemd timer, 03:45) | Source→Side Router Config Drift |
| sync-subscribe | li-home-0 (systemd timer, 03:30) | Source→Public Subscription Config Drift |
Config Derivation Pipeline
There are three places in the house needing mihomo config: Local Machine (TUN self-proxy), N100 (Whole-house transparent proxy), Public Subscription (Mobile roaming import). The node lists, proxy groups, and split rules must be consistent across all three, but DNS, TUN, and inbound methods differ. Maintaining them independently would inevitably lead to drift.
Solution: Single source of truth (/opt/mihomo/mihomo_config/config.yaml, the only manually edited file, containing proxies/groups/rules sections) + two timer-driven derivation scripts.
#1 Local Machine: Directly uses the source. TUN device Meta + Local AdGuard + fake-ip + controller.
#2 N100 Side Router (sync-mihomo-to-router.sh, timer 03:45):
- Extracts only
proxies/proxy-groups/rulessections from source →/etc/nikki/profiles/home.yaml - Local machine
mihomo -tvalidates source → scp to N100 →nikki -tvalidates → Backup old file (with timestamp) → Auto-rollback on validation failure → Reload Nikki - DNS/TUN/sniffer are injected by N100 local UCI mixin, not entering the profile — these are target-end specific and should not be overwritten from the source
#3 Public Subscription (sync-subscribe.sh, timer 03:30):
- Outputs
clash.yaml(Clash import) +sub.txt(v2rayN base64 share link) - Local-machine sanitization: Remove
external-controller+secret(leak leads to control),tunentire section (phones don't need it),nameserver:127.0.0.1(roaming devices can't reach),PROCESS-NAMErules (phones never match) - DNS replaced with fake-ip + Public DoH, international domains resolved via exit node to prevent leakage
- Entry:
https://subscribe.liz6.com(Caddy file_server, path token auth)
The two timers are staggered by 15 minutes to avoid simultaneous writes to N100. If you modify the source and want immediate effect, manually run the corresponding script; scripts are idempotent.
11. Known Risks
| Risk | Impact | Mitigation |
|---|---|---|
| li-home-0 WiFi Single Point | High-availability services like rathole/DERP all go down | Long-term migration to wired N100; wifi-watchdog 30s self-heal is short-term bleeding stop |
| N100 Single Port Hairpin | Single-arm topology asymmetric return must disconnect | MASQUERADE + bypass_masq nft table ensures symmetric return |
| mihomo/AdGuard Process Down | Whole-house disconnection (DHCP gateway still points to .7) | nikki-failopen (procd) process-level self-heal |
| Main Router Hardware/Firmware Failure | Whole-house disconnection (PPPoE+NAT+DHCP all down) | No hot backup planned; original plan for N100 as main router not implemented |
| Optical Modem Bridged | ONT only does optical-electrical conversion, no dial-up/routing/NAT, public IP directly to main router | Basically not a failure source, no double NAT |
Detailed assessment and recovery procedures see network-recovery.md.
Change Log
| Date | Change |
|---|---|
| 2026-06-21 | N100 side router online, li-home-0 side router retired; DHCP gateway/DNS switched from .8 to .7; N100 fail-open + li-home-0 wifi-watchdog deployed |
| 2026-06-24 | Public subscription fix: Original direct copy of local config caused leakage of local DNS + management port secret + tun; sync-subscribe.sh added local-machine sanitization; Side router profile re-run refreshed; Added sync-mihomo-to-router.timer |
| 2026-06-27 | Disabled ICMP send_redirects: Cured Xianyu app freezing (root cause of dual-path PAWS packet loss storm); CN-IP firewall bypass online (bypass_china_mainland_ip=1 + table inet bypass_masq); tcp-concurrent + CN DNS changed to plaintext UDP 223.5.5.5; Node insights |
Related Docs
- rathole-tunnel.md — Tunnel Home Solution
- network-recovery.md — Failure Recovery Manual
- monitoring.md — Monitoring Architecture