OpenClash环境下副路由直连上网的组网方案

主路由跑着 OpenClash 代理,但 NAS、IPTV 盒子、智能家居等设备需要直连上网,不走代理。在主路由上写分流规则容易失控,更稳妥的方案是拉一台副路由独立组网,流量与主路由彻底隔离。

1. 方案原理

从主路由上划出一个独立网段给副路由,副路由流量经主路由转发,从 WAN 口出互联网。配合 nftables 绕过规则,让这个网段的流量跳过 OpenClash 的劫持。

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TB
    subgraph WAN [🌐 互联网]
        ISP["运营商"]
    end

    Modem["光猫 192.168.1.1"]

    subgraph MainRouter [主路由 ImmortalWrt]
        PPPoE["PPPoE 拨号,获得公网 IP"]
        NFT{"nftables prerouting"}
        OC["OpenClash 代理"]
        BRLAN["br-lan(lan zone)192.168.6.1/24"]
        LAN4["lan4(direct zone)192.168.7.1/24"]
    end

    subgraph SubRouter [副路由 WAN: 192.168.7.2]
        SWAN["WAN 静态 IP"]
        SLAN["LAN: 192.168.5.1/24,DHCP 开启"]
        SWiFi["Wi-Fi / 有线设备"]
    end

    ISP --> Modem
    Modem --> PPPoE
    PPPoE --> NFT
    NFT -->|"其他流量 jump openclash"| OC
    OC --> BRLAN
    NFT -->|"192.168.7.0/24 accept(绕过)"| LAN4
    LAN4 -->|"网线"| SWAN
    SWAN --> SLAN
    SLAN --> SWiFi

    classDef direct fill:#2d6a4f,stroke:#1b4332,color:#fff
    classDef proxy fill:#e63946,stroke:#c1121f,color:#fff
    classDef decision fill:#e9c46a,stroke:#d4a843,color:#000
    classDef modem fill:#457b9d,stroke:#1d3557,color:#fff

    class LAN4,SWAN direct
    class OC proxy
    class NFT decision
    class Modem modem

关键路径:副路由流量在 nftables prerouting 链中被绕过规则放行,直达 WAN,跳过 OpenClash。没有绕过规则的话,这些流量同样会被 OpenClash 接管。

2. 分步配置

整个方案分 8 步。主路由用 ImmortalWrt(OpenWrt 分支),副路由用普通路由器即可。

前提:主路由已通过 PPPoE 拨号上网,LAN IP 为 192.168.6.1

2.1 规划 IP 地址

动手前先把 IP 规划好,避免地址冲突:

网络网段用途
主路由 LAN192.168.6.0/24常规设备(走代理)
主副互联192.168.7.0/24主路由 lan4 ↔ 副路由 WAN
副路由 LAN192.168.5.0/24副路由下的设备(直连)

2.2 分离 LAN 口

目标:把 lan4br-lan 桥里拆出来,变成独立端口。

操作路径:网络 → 接口 → 设备(Devices)

找到 br-lan 桥接设备,点 “ 编辑 “。

br-lan 桥接设备编辑页面,网桥端口只保留 lan1、lan2、lan3

在网桥端口列表中,取消勾选 lan4,只保留 lan1、lan2、lan3。点击「保存并应用」。

此时 lan4 物理口上的设备会断网——这是正常的,因为已经从主 LAN 中脱离了。如果你当前通过 lan4 连接主路由,请先换到其他 LAN 口或 Wi-Fi 再操作。

2.3 创建 Direct 接口

操作路径:网络 → 接口 → 添加新接口

填写参数:

参数说明
名称direct给这个独立网段起个名字
协议静态地址手动指定 IP
设备lan4刚才拆出来的那个口

添加新接口对话框,名称 direct,协议静态地址,设备 lan4

IPv4 地址配置:

  • IPv4 地址:192.168.7.1
  • 子网掩码:255.255.255.0

DHCP 服务器不要开启。副路由 WAN 用静态 IP,不需要主路由分配地址。

防火墙设置:在接口的「防火墙设置」标签页,选择「创建新的防火墙区域」,名称填 direct

接口 direct 的防火墙设置,创建/分配防火墙区域为 direct

这是 OpenWrt guest 网络的标准做法——每个独立用途的接口都给单独的 firewall zone。接口和防火墙区域准备好了,下一步配置 zone 的转发策略。

2.4 配置防火墙区域

操作路径:网络 → 防火墙

找到刚创建的 direct 区域,点 “ 编辑 “。

策略设置:

选项含义
Input(入站)拒绝 REJECT副路由网段不能访问主路由本机服务
Output(出站)接受 ACCEPT允许主路由向这个网段发包
Forward(转发)拒绝 REJECT默认不允许跨区域转发

转发规则:

  • ✅ 允许转发:direct → wan(让副路由的流量能出互联网)
  • ❌ 不要添加:direct → lan(不让副路由访问主 LAN 的设备)

防火墙区域设置页面,入站拒绝、出站接受、区域内转发拒绝,允许转发到目标区域 wan

这就是 OpenWrt guest/direct 网络的标准思路:独立 zone,默认隔离,只放行到 wan。

关于 NAT(IP 伪装):你可能注意到 direct zone 里没有勾选「IP 动态伪装(MASQUERADE)」。这不影响上网——副路由的流量 forward 到 wan zone 后,会由 wan zone 的 MASQUERADE 规则统一做源地址转换,不需要在 direct zone 额外开启。

2.5 物理连接

把网线从主路由的 lan4 口插到副路由的 WAN 口:

1
主路由 LAN4(direct 接口,192.168.7.1)──网线──▶ 副路由 WAN 口

这一步之后,lan4 不再是普通的 LAN 口,而是 direct 专用口。物理线路通了,接下来配置副路由的网络参数。

2.6 配置副路由

登录副路由后台(连副路由 Wi-Fi,访问 192.168.5.1)。进入 WAN 设置页,上网方式改为静态 IP(Static):

参数说明
IP 地址192.168.7.2和主路由 direct 同网段
子网掩码255.255.255.0
网关192.168.7.1指向主路由的 direct 接口
首选 DNS223.5.5.5阿里 DNS(公网 DNS)
备用 DNS1.1.1.1Cloudflare DNS

副路由 WAN 设置页面,静态 IP 配置

⚠️ DNS 必须填公网地址(如 223.5.5.5),不能填主路由 IP。 原因:主路由的 dnsmasq 会在 prerouting 阶段对所有 53 端口流量做 redirect to :53,劫持后 DNS 查询会走 OpenClash 代理解析——这恰恰是我们要绕过的。填公网 DNS + 配合后面的 dnsmasq 绕过规则,才能让 DNS 查询直达公网。

副路由 LAN 侧不需要改动,保持 LAN IP 192.168.5.1、DHCP 开启、Wi-Fi 继续发 192.168.5.x 段地址即可。

副路由 LAN 配置保持默认

2.7 测试验证

按以下清单逐项验证:

测试项预期结果不通过怎么办
连副路由 Wi-Fi拿到 192.168.5.x检查副路由 DHCP 是否开启
副路由 WAN 状态IP 192.168.7.2,网关 192.168.7.1检查静态 IP 和网线连接
打开网页能正常上网检查 DNS 和防火墙转发规则
主路由查看流量副路由流量从 direct 区域走 wan 出去检查 zone 转发设置

基础组网验证通过后,还有一个关键问题:主路由装了 OpenClash,副路由的流量仍然会被劫持。

3. OpenClash 绕过规则

主路由的 OpenClash 会劫持 TCP/UDP 流量做代理。副路由的 192.168.7.0/24 网段也在劫持范围内——需要添加 nftables 规则让这个网段的流量跳过 OpenClash。

下面这张图展示了数据包经过 nftables 各链时,有无绕过规则的不同走向:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
    A["副路由数据包进入主路由"] --> B{"mangle_prerouting 链"}
    B -->|"src = 192.168.7.0/24 → accept"| C["放行"]
    B -->|"其他来源"| D["jump openclash_mangle(代理)"]

    C --> E{"dstnat 链"}
    E -->|"src = 192.168.7.0/24 → accept"| F["放行"]
    E -->|"其他来源"| G["jump openclash(代理)"]

    F --> H{"DNS 查询(dport 53)?"}
    H -->|"是"| I{"dnsmasq prerouting 链"}
    H -->|"否"| J["forward → wan → 直连互联网"]
    I -->|"src = 192.168.7.0/24 → accept"| J
    I -->|"其他来源"| K["redirect to :53(DNS 劫持)"]

    classDef success fill:#10B981,stroke:#059669,color:#fff
    classDef danger fill:#EF4444,stroke:#DC2626,color:#fff
    classDef decision fill:#F59E0B,stroke:#D97706,color:#fff
    classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff

    class A primary
    class B,E,H,I decision
    class C,F,J success
    class D,G,K danger

3.1 添加绕过规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 绕过 OpenClash 的 mangle 链(透明代理入口)
nft insert rule inet fw4 mangle_prerouting \
ip saddr 192.168.7.0/24 accept \
comment "bypass-openclash-for-direct"

# 绕过 OpenClash 的 dstnat 链(端口劫持)
nft insert rule inet fw4 dstnat \
ip saddr 192.168.7.0/24 accept \
comment "bypass-openclash-for-direct"

# 绕过 dnsmasq 的 DNS 劫持(UDP + TCP)
nft insert rule inet dnsmasq prerouting \
ip saddr 192.168.7.0/24 udp dport 53 \
counter accept comment "direct-dns-bypass-udp"

nft insert rule inet dnsmasq prerouting \
ip saddr 192.168.7.0/24 tcp dport 53 \
counter accept comment "direct-dns-bypass-tcp"

⚠️ 必须用 nft insert(不是 nft add)。 insert 把规则插到链首,确保在 OpenClash 的 jump 规则之前执行。

3.2 验证规则

添加后逐条检查,确认 accept 规则在 OpenClash 的 jump 规则之前:

1
2
3
4
5
6
7
8
nft -a list chain inet fw4 mangle_prerouting
# 预期:ip saddr 192.168.7.0/24 accept 在 jump openclash_mangle 之前

nft list chain inet fw4 dstnat
# 预期:ip saddr 192.168.7.0/24 accept 在 jump openclash 之前

nft -a list chain inet dnsmasq prerouting
# 预期:两条 accept 规则在 redirect to :53 之前

终极验证——在副路由下的设备上运行:

1
curl ip.sb

返回的应该是你的真实公网 IP(和光猫/主路由 WAN 口的公网 IP 一致),而非代理 IP。

4. 常见问题

Q1:direct zone 的 Input 设成 REJECT,改成 ACCEPT 会怎样?

Input 控制的是「外部设备访问路由器本机服务」的权限。设成 REJECT,副路由网段就不能访问主路由上的 SSH、LuCI 管理页面、dnsmasq 等本机服务。如果改成 ACCEPT,副路由下的任何设备都能直接访问主路由后台——等于给了一个「不受信任」网段管理员权限,隔离形同虚设。

Q2:副路由 WAN 的 DNS 为什么不能填 192.168.7.1?

两层原因叠加:

  1. dnsmasq 劫持:主路由的 dnsmasq 在 prerouting 阶段会对所有 53 端口流量做 redirect to :53,把 DNS 查询拦截到主路由本地处理。被拦截的查询会走主路由的 DNS 解析链路——如果装了 OpenClash,就会走代理解析,违背「直连上网」的初衷
  2. Input=REJECT 兜底:即使 DNS 查询直接发往 192.168.7.1:53(而非被劫持),由于 direct zone 的 Input 策略是 REJECT,这个请求也会被防火墙拒绝

所以 DNS 必须填公网地址(如 223.5.5.5),配合 dnsmasq 绕过规则,让查询走 forward 路径直达互联网。

Q3:不加 nftables 绕过规则,副路由网段的流量会走什么路径?

以副路由发起一个 HTTPS 请求为例,数据包经过主路由时会被三层拦截:

  1. mangle_prerouting 链:OpenClash 的 jump openclash_mangle 把 UDP 流量导入透明代理
  2. dstnat 链:OpenClash 的 jump openclash 对 TCP 流量做 DNAT,确保流量走代理通道
  3. dnsmasq prerouting 链:所有 53 端口流量被 redirect to :53,DNS 查询被 dnsmasq 接管,解析结果可能是 OpenClash 返回的 fake-ip

最终效果:副路由下的设备「以为自己在直连上网」,实际上所有流量都经过了 OpenClash 代理——和挂在主路由 LAN 下没有区别,白折腾了。