一根网线,两张网络:OpenWrt 单线复用 IPTV 与全屋透明代理实战
本文整理于 2026 年 9 月 27 日,基于一套实际运行的家庭网络。接口名与 VLAN ID 属于这个案例;配置片段用于解释结构,不能脱离现有配置整段覆盖。
家里的网络升级,通常卡在布线而不是设备性能上。这次的限制很具体:弱电箱到电视墙只有一根网线,光猫的 IPTV 口在弱电箱,机顶盒却在电视柜,中间还隔着一台双网口 NAS。同时,我希望手机、电脑、电视都能无感地用上代理,并且在代理失效时,国内网站、NAS 和 IPTV 不受影响。
解决办法分两层:在同一条链路上用 VLAN 分别承载普通网络与 IPTV;在 OpenWrt 上只接管普通 LAN 的三层流量,实现国内直连、境外代理,IPTV 保持运营商到机顶盒的二层通道。
最终,电视不用再加一根线,客户端也不用安装任何代理软件。真正需要仔细设计的是三件事:VLAN 边界、流量分流,以及代理故障后由谁恢复网络。
文章按施工顺序展开:物理接线与 VLAN 规划 → OpenWrt 桥接 → PVE 与 NAS 透传 → 透明代理分流 → 故障恢复 → 规则更新与自动化。每一步都给出配置结构、判断依据和可执行的检查方法。
环境与版本
| 项目 | 配置 |
|---|---|
| 软路由 | N5095 主机,PVE 虚拟机运行 OpenWrt 25.12.5(内核 6.12.94) |
| 代理核心 | sing-box 1.13.21,TUN 透明分流 |
| NAS | 飞牛 OS,双 2.5G 网口,Open vSwitch 桥接 |
| 交换机 | 8 口千兆网管交换机,VLAN 1 / 48 |
| 无线 | 华为无线路由器,仅作 AP,网关仍指向 OpenWrt |
| 上游接入 | 光猫 WAN / IPTV 分别接 OpenWrt eth3 / eth4 |
从实际接线开始
改造前的环境很具体:软路由放在弱电箱,NAS、机顶盒和交换机放在客厅电视处,两个位置之间只有一根网线。 软路由和 NAS 的相关有线端口都是 2.5G,电视处另有一台 8 口千兆网管交换机。既要把普通家庭网络送到客厅,又要让机顶盒接入光猫的 IPTV 专口,同时保留访问 NAS 的高速通路。
图 1:弱电箱到电视处的唯一网线先接 NAS,再由 NAS 接千兆交换机。蓝色表示普通网络,橙色表示 IPTV;共享链路上 LAN 不带标签,IPTV 带 VLAN 48 标签。2.5G 标注表示端口规格,并非此次实测吞吐;链路实际双向通信。
硬件组成:N5095 主机运行 PVE,OpenWrt 是其中的虚拟机,四个以太网接口分别连接普通 LAN 和 PPPoE WAN;另有一块 USB 网卡直通给 OpenWrt,专门接光猫 IPTV 口。NAS 是刷了飞牛 OS 的双网口设备,通过 Open vSwitch 桥接两侧网口。
| OpenWrt 接口 | 作用 | 下游或上游 |
|---|---|---|
| eth0 | 普通 LAN | PVE 管理、电脑等 |
| eth1 | 复用上联 | PVE 桥 → NAS OVS → 网管交换机 |
| eth2 | 普通 LAN | 华为无线路由器,仅作 AP 使用 |
| eth3 | 普通 WAN | 光猫上网口,PPPoE 拨号 |
| eth4 | IPTV 专用入口 | USB 网卡,直连光猫 IPTV 口 |
管理网段为 192.168.100.0/24,网关是 OpenWrt。交换机端口 1 上联 NAS,端口 2 接机顶盒,其他端口继续提供家庭 LAN。NAS 在这里同时承担存储服务与链路桥接,因此 NAS 关机也会中断其后方网络——这是这套拓扑的真实依赖。
无线覆盖交给一台华为无线路由器,它通过 eth2 方向的普通 LAN 有线上联,只作为 AP 使用。手机、笔记本等无线设备接入同一家庭网络,网关仍是 OpenWrt,也沿用同一套透明代理分流。
为什么先接 NAS,再接交换机?
常见接法是让墙内网线先进入千兆交换机,再从交换机连接 NAS。这样一来,弱电箱一侧的设备访问 NAS 必须经过千兆交换机,路径上限被压到 1G;即使软路由和 NAS 都有 2.5G 网口,这段能力也发挥不出来。
现在的顺序是 软路由 → 唯一墙内网线 → NAS → 千兆交换机。访问 NAS 的流量在 NAS 终止,不经过后方的千兆交换机;NAS 的另一块网卡桥接给电视、机顶盒等客厅设备,继续透传普通 LAN 与 IPTV VLAN 48。
| 访问路径 | 网络带宽边界 |
|---|---|
| 弱电箱一侧、具备足够带宽的客户端 → 软路由 → NAS | 保留使用 2.5G 通路的可能,避免被客厅千兆交换机限制 |
| 千兆交换机后的客户端 → NAS | 仍受客户端端口和千兆交换机限制,单端口上限为 1G |
| 千兆交换机后的客户端 → 弱电箱方向 | 经过 NAS 桥接,仍受千兆交换机及共享上联限制 |
这样安排是为了充分利用已有的 2.5G 端口,保留其他设备访问 NAS 的速度上限,但不会把交换机后的千兆设备变成 2.5G。实际能跑到多少,取决于两端协商速率、客户端接入、虚拟化与桥接性能,以及 NAS 磁盘和文件协议;本文没有把端口规格当作传输速度测试结果。
另外说明一点:这里的“单线复用”指家庭内部共享上联链路。光猫的普通上网口和 IPTV 口仍然分别接入 OpenWrt,并不是把两项业务并到了同一个物理入口。
VLAN 48 是家庭内部的运输标签
光猫 IPTV 口交给 USB 网卡的是不带 VLAN 标签的以太网帧。选择 VLAN 48,只是为了在家庭内部把这条业务与普通 LAN 分开,它不代表运营商在光纤侧使用的业务 VLAN 编号。
图 2:机顶盒和光猫都收发不带标签的帧;VLAN 48 只出现在家庭共享链路上。普通 LAN 在同一链路上保持不带标签。
Tagged、Untagged 和 PVID 可以分开理解:
- Tagged:属于该 VLAN 的帧从端口发出时保留标签,适合连接另一台能处理标签的设备。
- Untagged:属于该 VLAN 的帧从端口发出时去掉标签,适合连接普通终端。
- PVID:端口收到不带标签的帧时,将它归入哪个 VLAN。
所以机顶盒端口需要同时满足“VLAN 48 Untagged”和“PVID 48”。只改 PVID 而没有把端口加入 VLAN 48,构不成完整通路;同时要把端口 2 从 VLAN 1 中移除,避免它继续接入家庭广播域。
| 交换机端口 | VLAN 1 | VLAN 48 | PVID |
|---|---|---|---|
| 1,上联 NAS | Untagged | Tagged | 1 |
| 2,机顶盒 | 非成员 | Untagged | 48 |
| 3–8,普通设备 | Untagged | 非成员 | 1 |
这是一条混合承载链路:普通 LAN 不带标签,IPTV 带标签。不同厂商的界面可能叫 Hybrid、General 或带 Native VLAN 的 Trunk,判断依据应是上表里的实际收发行为。
OpenWrt:用二层桥接把光猫 IPTV 口延伸出去
图 3:br-lan.1 是家庭网络的三层入口;br-lan.48 与 eth4 加入 br-iptv,把运营商网络延伸到机顶盒。两者没有三层互转。
普通 LAN 的底层桥是 br-lan,开启 VLAN filtering:VLAN 1 在 eth0、eth1、eth2 上不带标签;VLAN 48 只在 eth1 上带标签。OpenWrt 的管理地址配置在 br-lan.1,而不是把全部业务混在原始桥上。
下面是 /etc/config/network 中与 VLAN 相关的结构示意。已有同名桥时应修改原段落,不能重复创建;启用 VLAN filtering 和移动管理接口前,要准备好控制台或定时回滚。
config device
option name 'br-lan'
option type 'bridge'
option vlan_filtering '1'
list ports 'eth0'
list ports 'eth1'
list ports 'eth2'
config bridge-vlan
option device 'br-lan'
option vlan '1'
option local '1'
list ports 'eth0:u*'
list ports 'eth1:u*'
list ports 'eth2:u*'
config bridge-vlan
option device 'br-lan'
option vlan '48'
option local '1'
list ports 'eth1:t'
config device
option name 'br-iptv'
option type 'bridge'
option ipv6 '0'
option igmp_snooping '0'
list ports 'eth4'
list ports 'br-lan.48'
config interface 'iptv'
option device 'br-iptv'
option proto 'none'
u* 表示该桥端口在对应 VLAN 上不带标签,并作为入口 PVID;t 表示带标签。local=1 让主机侧能够通过相应 VLAN 子接口参与这个 VLAN,但它不会给 IPTV 分配地址。
普通 lan 接口使用 br-lan.1,沿用原来的静态地址、DHCP 和 IPv6 设置。iptv 没有路由器 IP,不申请默认路由,不提供家庭 DHCP,也不做 NAT;机顶盒的地址、DNS、认证和组播都来自运营商。防火墙不开放 LAN 与 IPTV 之间的三层转发。
本案例最初关闭 br-iptv 的 IGMP snooping,是为了先验证完整透传,代价是组播可能在 VLAN 48 的成员端口之间泛洪;本例只有一条机顶盒通路,影响范围有限。以后增加多个 IPTV 端口时,应单独验证 snooping 与查询器行为,不应把“关闭组播侦听”当成通用优化。
这样的接线通常不需要 igmpproxy 或 udpxy,二层透传已经能让机顶盒直接参与运营商业务。如果光猫没有独立 IPTV 口、上游仍带业务标签,或者目标是让普通播放器观看组播,方案需要另行设计。
PVE 和 NAS 都必须保留 VLAN 标签
中间隔着 NAS,并不会自动改变业务类型;关键在于它是否真正桥接,以及标签有没有被过滤或改写。
实际的 PVE 路径包含虚拟网卡、虚拟机防火墙桥、Linux 桥和物理网卡。本案例中,相关桥没有启用 VLAN filtering,虚拟网卡也没有单独配置 access tag,VLAN 48 已通过抓包和机顶盒 DHCP 验证穿过整条链路。其他 PVE 安装如果启用了 VLAN-aware bridge,应核对允许的 VLAN 和虚拟机网卡设置,不能仅凭桥名相同就假设行为一致。
NAS 的 OVS 桥包含:
Bridge enp4s0-ovs
Port enp4s0 ← OpenWrt / PVE 方向
Port enp5s0 → 交换机方向
Port enp4s0-ovs ← NAS 的内部管理接口
先检查,而不是直接改:
sudo ovs-vsctl show
sudo ovs-vsctl --columns=name,vlan_mode,tag,trunks list Port
本例中 name、vlan_mode、tag、trunks 三个字段都为空。在 OVS 正常的交换逻辑下,没有 tag 时默认按 trunk 处理,空 trunks 表示不限制 VLAN,这与“空配置代表不支持 VLAN”的直觉相反。自定义 OpenFlow 流表可能改变实际行为,所以还需要用数据包验证。OVS 官方 VLAN 配置说明
NAS 的两块网卡不应理解成两台路由器。桥接通路不需要给每个物理端口都配置 IP,管理 IP 放在桥的内部接口上通常更清晰。本案例保留了用户原有的双管理地址,但这不是 IPTV 透传的必要条件。
全屋透明代理:只接管普通 LAN
IPTV 通路跑通之后,再在 OpenWrt 上增加代理。两部分最关键的交点是 br-lan.1:家庭客户端由它进入路由器,IPTV 则从独立二层通路经过,不进入 TUN。
图 4:全屋透明代理覆盖普通 LAN 的 IPv4 / IPv6 TCP、UDP。国内和本地流量优先直连,IPTV 不进入代理。图中的域名分流以规则识别到域名为前提。
本案例使用 sing-box 1.13.21。TUN 接口为 sb-tun0,启用 auto_route 与 auto_redirect,接管接口限定为 br-lan.1。下面只是参数摘录,缺少完整路由、DNS、规则集和节点配置,不能单独运行:
{
"type": "tun",
"tag": "tun-in",
"interface_name": "sb-tun0",
"address": ["172.31.255.1/30", "fd7e:48:ffff::1/126"],
"mtu": 1500,
"auto_route": true,
"auto_redirect": true,
"strict_route": false,
"include_interface": ["br-lan.1"],
"iproute2_table_index": 20248,
"iproute2_rule_index": 9048
}
这些功能和接口限定的语义以对应版本的 TUN 文档为准。示例地址、表号和标记都要与现有网络检查冲突,最终用本机 sing-box check 验证;不要将新版本文档中的字段直接抄进旧核心。
流量在进入代理前分类:私网、管理地址、组播、国内 IPv4 / IPv6 地址优先直连;国内域名的解析结果还会由 dnsmasq 写入动态 nftables 地址集合,用于覆盖部分使用境外 CDN 的国内业务。
这比只在 sing-box 内部配置一个 direct 出站更重要:如果“直连”也必须先进入已经故障的代理进程,国内网络仍会被拖累。因此代理之外保留独立的直连规则,由较早执行的 nftables 分类和策略路由送回系统主路由表。
路由器自身的出站也走普通 WAN,避免节点连接和探测形成代理环路。这里的“全屋”只指普通 LAN 客户端,不能据此认为 OpenWrt 上所有程序都经过代理;ICMP 也不在覆盖范围内,不能用境外 ping 判断代理是否可用。
普通境外业务通过两个订阅的自动选择组。Google、YouTube、OpenAI 使用单独的非香港出口:首选一个新加坡节点,失效后切到其他非香港节点。这是本案例的出口选择,其他家庭应根据实际业务连通性和服务要求决定。
专用 DNS 跟随相同出口,减少解析位置与访问出口不一致的问题。但地区正确不保证服务接受该 IP,某些服务的 403 可能来自出口信誉或风控。域名分流依赖规则匹配与嗅探,直连 IP 或无法识别域名时会回落到默认组。
节点配置保留了订阅提供的跳过 TLS 证书校验选项,这是尚待处理的安全边界,不应照搬为推荐默认值。部署自己的节点时,应使用正确的服务器名称和有效证书,并尽量保持证书验证开启;DoH 上游的 HTTPS 校验与节点隧道的证书校验相互独立。
节点切换与核心恢复:两类故障,两套监控
图 5:上半部分处理出口节点失效,下半部分处理整个代理核心异常。两套监控独立运行,但共享维护锁,更新配置时不会同时切换出口或撤销路由。
这套配置没有把所有事情塞进一个定时任务,而是分成三个入口:
| 脚本 | 主要职责 | 运行方式 |
|---|---|---|
/usr/sbin/proxy-nonhk-priority |
新加坡首选与非香港备用之间切换 | procd 托管的常驻服务 |
/usr/sbin/proxy-guard |
保持国内直连,代理核心异常时降级,出口恢复后重新接管 | procd 托管的常驻服务 |
/usr/sbin/proxy-rules-update |
下载规则、生成配套文件、提交更新,失败时恢复旧版 | cron 每日触发,也可手动运行 |
前两个处理运行中的故障,第三个处理配置更新。它们共同使用 /usr/lib/proxy-common.sh,把加锁、原子复制、暂停判断和探测进程退出这些基础动作统一起来。
首选节点:用 selector 表达优先级
“始终优先使用新加坡节点”不能只靠 URLTest 实现。URLTest 的作用是在候选中选择延迟较低的一个,并不保证选中指定节点。因此这里设置一层 selector,成员只有首选节点和 nonhk-backup;备用组内部再由 URLTest 在其他非香港节点中选择。
监控使用两个只监听本机的入口:127.0.0.1:17892 固定走首选,127.0.0.1:17893 走备用组。即使当前 selector 已经选了备用,监控仍可以单独检查首选是否恢复,不用先把家庭流量切回去试。
每轮先读取本机 Clash API 的 selector 状态,再从首选入口依次请求两个 HTTP 204 地址。任意一个返回 204 就算本轮成功;连接超时为 3 秒,单次请求最多 6 秒。判断依据是实际请求成功,而不是 API 返回了一个延迟数字。
切换逻辑如下:
当前使用首选
├─ 本轮成功:失败计数清零
└─ 连续两轮失败:探测备用,再切换到备用组
当前使用备用
├─ 首选失败:成功计数清零,继续用备用
└─ 首选连续三轮成功:切回首选
每轮结束后等待 30 秒
两次失败、三次成功,是为了让退出和回切使用不同阈值,减少短时抖动造成的来回切换。这里的 30 秒是每轮结束后的等待,不是包含请求耗时的固定周期,所以切换不能简单理解成精确的 60 秒或 90 秒。
切换前会检查备用入口;如果首选与备用都失败,也会选择备用组并记录双路径异常,让备用 URLTest 继续尝试其他节点。这不是“备用一定可用”的承诺。API 不可用或 selector 返回意外状态时,本轮只记录异常,不盲目更改选择。
selector 切换不主动关闭已有连接,因此它主要影响新连接。旧节点已经失效的长连接,并不会仅靠改变 selector 就自动恢复。
核心故障:直连规则必须独立存在
proxy-guard 与 sing-box 是两个独立进程。guard 的服务启动序号为 95,早于 sing-box 的 99;启动后先建立国内与本地流量的直连规则,再给核心 25 秒启动宽限。
国内直连依赖独立的 proxy_guard nftables 表,以及 IPv4 / IPv6 的策略规则:标记 0x4842 的流量通过优先级 8000 返回 main 表。即使核心尚未启动,这部分路径也可以存在。guard 每轮还会检查这些规则是否仍在,缺失时重新载入,并从 br-lan.1 的路由刷新 LAN IPv6 前缀集合。
宽限期之后,guard 检查两个条件:生产 sing-box 进程是否存在,以及常规代理入口 127.0.0.1:17890 能否完成 HTTP 204 请求。连续两轮异常才执行降级,每轮结束后等待 5 秒。两个目标、请求超时和维护锁都会影响实际检测时间,因此不能把它当成固定 10 秒恢复。
降级执行的是一串明确动作:
- 记录当前代理拥有的动态回环路由表。如果核心被挂起,先发送 CONT,让服务停止流程能够继续。
- 将 sing-box 的运行启用值置为 0,停止生产服务。
- 删除代理专属 nftables 表、预留的策略规则、表 20248 的路由和
sb-tun0;对动态回环表再次核对内容,避免删到其他业务。 - 清空指向已停止核心的域名转发列表,通过 HUP 让 dnsmasq 重新读取
servers-file,基础解析服务继续运行。 - 补齐独立直连规则,再检查核心、TUN 和代理策略规则是否已经退出,记录
degraded-direct状态。
这个过程不会清空整套防火墙,也不会恢复一份历史 DHCP 配置。普通 WAN、静态绑定、NAS 管理和 IPTV VLAN 48 应保持原有结构。
从降级状态自动恢复
停用生产核心后,如果只检查生产入口,结果永远是失败。因此 guard 使用独立的 /etc/sing-box/probe.json 临时启动探测进程,从 127.0.0.1:17891 检查代理出口,不让这次探测重新接管家庭 LAN。
失败后等待约 30 秒再尝试;成功后按以下顺序恢复:确保直连规则存在、检查生产配置、恢复域名转发列表、启用生产服务,再进入 starting 状态。经过新的 25 秒宽限与实际出口检查,才转为 active。
临时探测进程也有退出限制:先 CONT,再 TERM,最多等待 5 秒,仍未退出就 KILL,并回收子进程。这样处理,是为了防止一次挂起的探测堵住后续恢复。
运行启用值与用户意图分开保存。sing-box.main.enabled 在故障期间可能临时为 0,而 /etc/proxy/desired-enabled 仍为 1,表示用户希望恢复代理。手动关闭时才将后者设为 0,避免监控把用户主动停用的服务重新拉起来。
规则自动更新:准备、提交、回滚
图 6:下载和校验先在临时目录完成,提交阶段持有维护锁。事务记录清除之前,任何失败都按旧版备份恢复。
规则更新与节点订阅刷新是两件事。这里每天自动更新的是分流规则,节点订阅目前仍单独维护;更新域名库不会自动增加节点或改变首选身份。
下载六份规则,生成三类配套文件
proxy-rules-update 从 MetaCubeX 的 sing 规则分支下载六份 .srs:国内 IP、国内域名、境外域名,以及 OpenAI、Google、YouTube 的域名规则。
运行前先取得更新脚本自己的单实例锁,再检查代理入口是否可用。出口不通就退出,不为了下载规则反复重启核心。下载全部放在 /tmp/proxy-rules.* 临时目录中,每个请求有时间上限,只有 HTTP 200 且文件非空才继续。
如果六份文件与本地副本相同,正常更新直接结束,避免没有变化也重启服务。需要处理变化时,脚本反编译规则,提取 IPv4 / IPv6 网段与域名,去重并合并,生成:
- 国内域名配置:选择直连解析上游,同时维护域名解析结果对应的 nftables 地址集合。
- 境外域名转发列表:交给本机 sing-box DNS 入口。
guard.nft:包含国内网段和直连分类逻辑,供独立 guard 使用。
因此规则不是只换几份 .srs。核心内的路由规则、dnsmasq 的域名处理和核心外的直连集合,需要作为一次更新同步推进。
先检查候选,最后才动生产文件
提交前有三层检查:nft -c 检查生成的防火墙规则,dnsmasq --test 检查域名配置,sing-box check 检查引用新规则的候选配置。候选配置把规则路径指向临时目录,生产文件在这一步仍保持原样。
下载、反编译或检查失败,都在提交前结束。准备阶段不长期占用共享维护锁,guard 仍能处理运行故障;真正提交前,再取得维护锁并重新确认用户仍启用代理、生产服务没有处于降级状态。
提交时记录事务,不只复制文件
正式替换前,脚本在 /root/proxy-backups/auto-rules-* 创建受保护备份,复制旧规则与配套文件,并逐项比较备份是否完整。除了磁盘文件,还保存当前运行中的 cn4、cn6 集合,避免文件回滚了、内核里的分类仍停在新版本。
备份清单记录“生产路径对应哪个备份文件”。随后将备份目录写入 /etc/proxy/rules-transaction,再开始替换。这个标记表示更新尚未完成,不是普通日志。
单个文件的安装使用同目录临时文件,再通过 mv 原子替换,避免进程读到只写了一半的内容。多份文件之间仍不可能靠一次 mv 全部原子切换,所以还需要维护锁、事务标记与恢复流程。
替换完成后,脚本按顺序检查生产配置、重启 sing-box、等待本机 API 就绪、完整重启 dnsmasq,再检查代理 HTTP 请求与本机域名解析。全部正常才刷新运行时国内 IP 集合,最后删除事务标记,宣告更新完成。
dnsmasq 在这里使用完整重启,是因为国内规则含有 nftset 等配置,HUP 不会重新加载这部分内容。前面核心降级只修改 servers-file,可以使用 HUP;两种情况不能混为一谈。完整重启会带来短暂服务切换,因此定时任务适合放在家庭使用较少的时段。
定时入口位于 /etc/crontabs/root,按路由器系统时区解释。每天一次的触发时间应与系统时区一起维护;例如系统使用 UTC 时,19:00 对应北京时间次日 03:00,不能换了时区却继续沿用同样的时间含义。
失败时自动回滚,异常中断后补做恢复
替换后的配置检查、服务启动、HTTP 请求、解析或运行时集合更新,只要有一步失败,就进入恢复流程:按清单还原旧文件,恢复旧的 cn4、cn6 集合,再重启相关服务。若代理已被用户关闭或处于降级状态,恢复也不会强行保留指向已停核心的域名转发。
脚本的 EXIT、TERM、INT 处理可以覆盖普通错误和可捕获中断,但 SIGKILL 与断电不会执行这些处理。因此事务标记保存在 /etc/proxy,而不是仅放在 /tmp。
guard 在启动和运行循环中都会查找遗留事务,调用 proxy-rules-update --recover 补做恢复。只有恢复完整后才清除标记;恢复不完整就保留备份路径并记录异常,不把“一部分文件复制成功”当成整体成功。
维护锁、服务管理与手动控制
三个脚本共享一把维护锁
/var/run/proxy-maintenance.lock 由更新、降级、恢复和节点切换共同使用。更新提交期间,两个监控拿不到锁,就跳过本轮操作;不会把重启中的核心误判成故障,又同时删除刚安装的规则。
每个常驻服务和更新脚本还有自己的单实例锁,防止重复启动。直连规则的快速刷新另有一把短锁,不需要一直等待整个下载过程。加锁使用 BusyBox 兼容的 flock -n 加有限重试,避免依赖当前系统没有的超时参数。
临时维护还兼容 /tmp/proxy-guard.pause。遗留暂停文件最多有效 15 分钟,过期自动清除,避免忘记删除一个标记就让监控永久停止。常规更新主要通过维护锁协调,不依赖人工留下暂停文件。
等待与探测子进程会主动关闭继承的锁文件描述符。否则主服务退出以后,旧的 sleep 或探测进程仍可能占着锁,新实例启动了却始终无法工作。
procd 托管,接口变化触发刷新
guard 与优先节点监控由 /etc/init.d/ 服务交给 procd 管理,异常退出后自动重新启动;脚本自身的实例锁负责防止重入。
/etc/hotplug.d/iface/95-proxy-guard 监听 wan、wan_6、lan 的上线与更新事件,防火墙 reload 也通过 include 调用 proxy-guard refresh。这些入口只补齐直连规则和刷新运行时 LAN 前缀集合,不会每来一次事件就重新下载订阅或规则,也不等于重建所有静态路由排除项。
配置文件权限也按读取者区分:节点凭据配置保持 root-only;dnsmasq 需要读取的域名文件使用 root:dnsmasq、640。原子复制与回滚都保持同样的权限,避免更新后文件存在,服务却读不到。
日常查看与控制
以下命令在 OpenWrt 上使用,不需要读取或展示节点凭据:
# 查看故障恢复与首选节点监控状态
/usr/sbin/proxy-guard status
/usr/sbin/proxy-nonhk-priority status
# 查看服务日志
logread -e proxy-guard
logread -e proxy-nonhk-priority
logread -e proxy-rules
# 临时关闭代理:保留基础网络,且不会自动拉起
/usr/sbin/proxy-guard off
# 重新启用代理,并恢复自动监控
/usr/sbin/proxy-guard on
# 只下载、生成和检查候选,不提交生产更新
/usr/sbin/proxy-rules-update --dry-run
# 手动更新;无变化时结束
/usr/sbin/proxy-rules-update
--dry-run 仍会联网下载,并不是只读取本地文件;--force 则会在没有规则变化时也走完整提交过程,包含服务重启,通常用于明确需要重新应用的情况。
guard 状态与心跳分别写入 /tmp/proxy-guard.state、/tmp/proxy-guard.heartbeat,首选监控也有对应状态与心跳。更新过程除写入 logread,还保存在 /root/proxy-staging/auto-rules.log;日志超过约 512 KiB 时,下次运行保留最后 200 行。/tmp 状态不是长期历史,重启后会重新生成。
停用代理与撤销部署是不同操作
proxy-guard off 适合暂时不用代理,配置与节点保留,之后可以用 on 恢复。/usr/sbin/proxy-rollback 则用于撤销本套代理部署:先备份当前配置,停用监控和事件入口,移除代理自己的 DNS 配置入口、防火墙项、直连标记规则与 cron 任务,同时保留当前 WAN、DHCP 绑定、IPTV 和其他定时任务。
撤销前还会核对被管理的防火墙项是否仍属于这套部署,发现含义已被修改就停止,避免误删别的配置。它不等于恢复到某一天的完整系统快照,也不是 off 的另一种写法;执行以后需要按部署流程重新接入相关配置。
订阅节点也采用同样的边界:身份表记录提供者、原名、协议、端点与 SNI 的稳定关系,订阅重排不会把“第 14 个节点”悄悄换成另一个首选。首选消失或端点变化时,构建器明确报错,避免重建节点配置后出口选择发生无提示变化。
小结
- 单线复用的关键在边界:PVID 与 VLAN 成员缺一不可;中间设备可以没有显式 VLAN 配置,但必须原样透传标签,并以抓包确认。
- NAS 放在交换机前换来了 2.5G 路径,代价是它成为后方网络的单点,这是需要知情的取舍,而不是无代价优化。
- 透明代理只接管
br-lan.1:路由器自身出站、IPTV 和 ICMP 都在覆盖范围之外,不能用境外 ping 判断代理是否可用。 - 直连规则必须独立于代理进程,故障恢复也必须独立于被恢复的核心,否则代理一挂,绕行和恢复会一起失效。
- 运行时故障与配置更新分开处理:节点切换、核心降级由常驻监控负责,规则更新走事务式提交,两者通过共享维护锁避免并发。
- 自动化更新按生产系统对待:单实例锁、候选校验、原子替换、备份与遗留事务恢复;完整重启 dnsmasq 有短暂切换,时间应挑家庭使用较少的时段。