米米的博客

做了一点微小的工作

项目地址:
astra-dns
luci-app-astra-dns

在 OpenWrt 上折腾 DNS,很多人最后都会走到一个相似的场景:我想让路由器负责全家的 DNS,同时希望它能做广告过滤,还希望访问 Cloudflare 站点时可以自动返回一个更快、更稳定的优选 IP。

单独看,这些需求都不罕见。

广告过滤可以交给 AdGuard Home。DNS 转发、分流、重写可以交给 MosDNS。Cloudflare 优选 IP 可以通过 CloudflareSpeedTest 之类的工具定期测速,再把结果写进 DNS 规则里。代理相关的域名处理,还可能再接入 Mihomo 等组件。

这些工具都很好,也都各自解决了真实问题。但当它们被串在一起时,整个 DNS 链路就会变得有些重。

一个典型的 motivating example

假设我们在 OpenWrt 上已经用了 MosDNS。MosDNS 很适合做 DNS 转发、分流和规则化处理,也可以把某些域名的解析结果改写到指定 IP。于是,如果我们想做 Cloudflare 优选 IP,一个自然的方案是:

  1. 用 CloudflareSpeedTest 定期测出当前网络下表现最好的 Cloudflare IP。
  2. 把这些 IP 写入 MosDNS 的 rewrite 规则。
  3. 当域名解析结果落在 Cloudflare 地址段时,让 MosDNS 返回优选 IP。

这条链路本身是合理的。

但问题来了:MosDNS 并不是广告过滤器。它可以做规则匹配和 DNS 编排,却不直接提供 AdGuard Home 那种完整的广告过滤体验。如果我们还想拦截广告域名,就往往需要再引入一个 DNS 服务,比如 AdGuard Home。

于是原本的需求变成了这样:

1
2
3
4
5
客户端
-> OpenWrt DNS 入口(dnsmasq)
-> AdGuard Home 负责广告过滤
-> MosDNS 负责分流、转发和 Cloudflare IP 重写
-> 上游 DNS

这当然能工作,很多成熟配置也正是这么搭起来的。但它带来的代价也很明显:

  • 查询链路变长,每个 DNS 请求都可能跨过多个进程。
  • 每个组件都有自己的配置文件、缓存、日志和排错方式。
  • 出现问题时,很难第一时间判断到底是广告规则、rewrite 规则、上游 DNS,还是服务转发顺序出了问题。
  • OpenWrt 设备资源有限,长期运行多个 DNS 服务并不总是优雅。
  • 对普通用户来说,理解整条链路的心智成本很高。

Astra DNS 正是为了解决这个具体场景而设计的:当你只是想在路由器上同时获得广告过滤、DNS 转发和 Cloudflare 优选 IP 重写时,能不能不要把多个 DNS 服务串起来?

阅读全文 »

中兴 F410GV9 恢复出厂设置后,Web 管理仍然可以正常访问,但 Telnet 会回到关闭状态。临时进入 FactoryMode 虽然能够取得 root shell,却只在当前运行周期内有效;设备重启或 FactoryMode 超时后,临时账号和 Telnet 服务都可能失效。

如果后续需要备份配置、排查服务或研究设备内部数据,更方便的做法是先通过 FactoryMode 开启一次临时 Telnet,再把 LAN 侧 Telnet 配置写入设备数据库。这样重启后可以使用自己设置的账号密码重新登录。

本文记录一次在 F410GV9 上实际验证成功的配置过程。Telnet 只对局域网开放,WAN 侧开关始终保持关闭。操作修改前建议备份现有配置。

环境与准备

本次设备和网络状态如下:

项目实测值
型号F410GV9
管理地址192.168.1.1
Web 管理HTTP 80
FactoryMode 服务HTTP 8080
Telnet 端口TCP 23
初始 Telnet 状态关闭
系统BusyBox 1.17.2
内核Linux 4.1.25,ARMv7
固件构建时间2021-08-19

电脑需要连接到光猫 LAN 侧,并与 192.168.1.1 处于同一网段。可以先确认设备在线,以及 Telnet 尚未开放:

1
2
ping 192.168.1.1
nc -vz 192.168.1.1 23

开启临时 Telnet 使用 zteOnu。准备好 Go 环境后直接使用上游源码即可:

1
2
3
git clone https://github.com/Septrum101/zteOnu.git
cd zteOnu
go test ./...

开启临时 FactoryMode Telnet

F410GV9 的普通 Web 管理位于 80,FactoryMode 接口位于 8080zteOnu 默认端口就是 8080,下面仍显式写出端口,避免把两个服务混淆。

本机实测可用的 FactoryMode 用户名是 factorymode,密码是 nE%jA@5b。这组凭据来自公开项目 zte_modem_tools 内置的默认候选表,并不是设备独有的密码,也不是 Web 管理账号。不同运营商和固件可能使用其他组合。

执行:

1
2
3
4
5
go run . \
--ip 192.168.1.1 \
--port 8080 \
--user factorymode \
--pass 'nE%jA@5b'

握手成功时,工具会依次完成 FactoryMode 复位、随机数交换、认证和模式切换,并返回一组临时 Telnet 凭据:

1
2
3
4
5
6
7
step [0] reset factory: ok
step [1] request factory mode: ok
step [2] send sq: ok
step [3] check login auth: ok
step [4] enter factory mode: ok
user: <TEMP_USER>
pass: <TEMP_PASSWORD>

输出中的 <TEMP_USER><TEMP_PASSWORD> 由设备为本次 FactoryMode 会话动态生成,每次运行都可能不同。这组账号也不是准备写入数据库的持久账号。FactoryMode 可能在数分钟后超时,拿到凭据后应尽快继续。

使用临时账号连接光猫:

1
telnet 192.168.1.1 23

成功后会看到设备型号和 BusyBox shell。先用只读命令确认权限:

1
2
id
uname -a

实测结果为:

1
2
uid=0(root) gid=0(root)
Linux F410GV9 4.1.25 #1 Thu Aug 19 13:16:08 CST 2021 armv7l GNU/Linux
阅读全文 »

家里的电信光猫使用路由模式,由光猫完成 PPPoE 拨号,下游再连接自己的路由器。这样的配置方式 IPv4 使用起来没什么问题,IPv6 也可以拿到地址,但是实际使用时却发现了一个问题:光猫已经从电信获得了一个/60 IPv6 前缀,直连光猫的设备也有 IPv6,但下游 OpenWrt 只能在 WAN 口获得一个/64地址,LAN 侧始终没有公网 IPv6 前缀。换句话说,光猫默认没有启用下游 IPv6 PD 前缀分发。这对于 Tailscale 之类需要打洞的场景不友好:OpenWrt 的 LAN 侧只能使用 ULA、NAT66 或 IPv6 Relay,也失去了利用原生 IPv6 进行 Tailscale 直连的条件。

把光猫改成桥接、由主路由拨号当然可以解决 IPv6 前缀分配问题,但 XG-140G-TF 本身的转发性能足够,而主路由是软路由,PPPoE 本身也非常吃单核性能,所以我还是希望保留光猫拨号。进一步检查后发现,XG-140G-TF 的固件中其实包含下游 DHCPv6-PD Server,只是电信配置默认没有开启对应的产品级开关。

本文记录如何在保留光猫拨号的情况下,开启 XG-140G-TF 的 IPv6 PD 前缀分发。光猫的管理员登录、Telnet 开启和 root 密码获取方法,请先参考配置 XG-140G-TF 光猫

问题现象

光猫的 IPv6 状态显示,WAN 连接已经通过 Prefix Delegation 从电信取得了/60前缀,例如:

1
2001:db8:1234:690::/60

一个/60可以划分为 16 个/64子网。在这个例子中,范围是:

1
2
3
4
2001:db8:1234:690::/64
2001:db8:1234:691::/64
……
2001:db8:1234:69f::/64

光猫使用第一个690::/64作为自己的 LAN 网段,通过 RA 广播给直连设备。但在修改前,下游 OpenWrt 的 WAN6 状态只有一个地址:

1
2001:db8:1234:690:6c59:4eff:fecb:61f4/64

容易产生误解的是,这只是 OpenWrt WAN 接口通过 SLAAC 获得的一个 IPv6 地址,并不是可供 OpenWrt 继续划分给 LAN 的前缀。检查 WAN6 状态时可以看到:

1
"ipv6-prefix": []

由于没有获得 IA_PD,OpenWrt 的 LAN 只能继续使用 ULA 地址,日志中还会出现:

1
A default route is present but there is no public prefix on lan
阅读全文 »

家里开通了电信 IPTV,但套餐只包含了一台机顶盒,家中有多台电视时使用起来并不方便。很早以前我就知道可以通过 igmpproxy 转发组播,再配合 udpxy 将组播转换为普通 HTTP 单播,但此前一直没有配置成功。

最近重新梳理了一遍光猫、机顶盒和组播之间的关系,终于实现了以下目标:

  • 原装 IPTV 机顶盒继续正常使用,直播和点播均不受影响;
  • 局域网中的其他设备(电视、电脑、手机等)可以收看直播。

本文记录的是我在湖北武汉电信网络中的实际配置。不同地区的 VLAN 和认证方式都可能不同,请不要直接照抄参数。修改光猫前,建议先备份配置,并记录每一处原始设置。

原始网络结构

光猫中原本有两项主要业务:

  1. 上网业务,采用路由模式,本文不讨论;
  2. iTV 业务,采用桥接模式,仅绑定到光猫的 LAN2 口。

iTV 桥接连接的关键参数如下:

项目原始设置
业务类型OTHER
连接模式桥接
IP 模式IPv4
业务 VLAN ID3542
端口绑定LAN2(iTV 口)

在这一结构下,电信机顶盒连接 LAN2,并由机顶盒自己进行 PPPoE 拨号。机顶盒的网络设置中可以看到 IPTV 账号,但密码通常不会直接显示。

这里有一个容易踩的坑:IPTV 密码可能由机房或装维系统下发。即使通过 10000 客服重置或查询到了一个密码,也不一定能在现网环境中成功拨号。如果确认账号无误但始终认证失败,最好直接联系当地装维人员,获取当前线路实际使用的 IPTV 密码。

失败的尝试:让两个设备分别拨号

我最初的思路很直接:在光猫的 iTV 桥接连接中,除 LAN2 外再绑定一个 LAN3,让原装机顶盒连接 LAN2,另一个路由器或小主机连接 LAN3。两台设备分别使用同一组 IPTV 账号、密码拨号,小主机负责运行 igmpproxy,就可以实现局域网组播。

实际测试发现,这种方式行不通:只要其中一台设备已经拨号成功,第二台设备的拨号就会被拒绝。至少在我的线路上,同一个 IPTV 账号无法同时建立两个 PPPoE 会话。这意味着如果光猫桥接,让下游设备拨号,原装机顶盒和独立组播代理只能二选一。

关键调整:改由光猫拨号

进一步查看机顶盒设置时,我发现它除了 PPPoE,还支持 DHCP 联网。由此可以换一种思路:

不再让机顶盒直接拨号,而是让光猫建立 IPTV PPPoE 连接,再通过 DHCP 给 LAN2、LAN3 下游设备分配地址。

我将光猫中的 iTV 连接从桥接模式改为路由模式,填写 IPTV PPPoE 账号和密码,并同时绑定 LAN2、LAN3。

修改后,光猫成功获得了运营商 IPTV 网络中的 10.x.x.x 地址,机顶盒改为 DHCP 后也能取得光猫下发的局域网地址。

此时测试结果如下:

  • IPTV 平台登录正常;
  • 菜单和业务页面正常;
  • 点播节目可以播放;
  • 直播频道仍然黑屏

这个现象很有价值。它说明 PPPoE 认证、DHCP 和普通 IP 通信已经正常,机顶盒也没有被限制为必须自行拨号;但直播所需的组播链路尚未打通。点播通常走普通单播,而直播依赖 IGMP 和运营商的组播 VLAN,因此两者完全可能出现一个正常、另一个黑屏的情况。

阅读全文 »

过去几年里,大模型把语音助手的想象空间重新打开了。我们已经习惯了在手机、网页和电脑上和 AI 对话,但家里的智能音箱却常常还停留在比较固定的指令式体验里:问天气、放音乐、控制设备可以,但想要更自然的连续对话、更灵活的工具调用,或者接入自己选择的大模型服务,就没那么顺手了。

xiaoai-agent 想解决的正是这个问题:让小爱音箱不只是一个云端语音入口,而是变成一台可以常驻运行的端侧语音 Agent 设备。

项目地址:github.com/stevenjoezhang/xiaoai-agent

背景:小爱音箱还有很多潜力

小爱音箱本身是一类很适合承载语音 Agent 的设备。它有麦克风阵列,有扬声器,有唤醒能力,也有成熟的音频播放和 TTS 链路。换句话说,它的硬件形态已经非常接近「家里的 AI 入口」。

但现实使用中会遇到几个限制:

  • 原生小爱同学的对话能力和可扩展性有限,很难按自己的偏好接入新的大模型、ASR 或工具。
  • 一些改造方案需要额外部署服务器或桥接服务,链路变长,维护成本也会变高。
  • 原生小爱同学仍然可能监听麦克风,出现抢麦、抢答,甚至触发小米云端控制的情况。
  • 音频体验很难做好,比如唤醒、录音、回声消除、连续对话、中途打断等细节,任何一个环节不稳都会影响日常可用性。

xiaoai-agent 的方向,是尽量把这些能力收回到音箱端侧,让音箱自己完成唤醒、录音、识别、思考、工具调用和回复。

现有方案与不足

xiaoai-agent 之前,已经有不少项目尝试把小爱音箱和大模型连接起来。这些探索证明了小爱音箱的可玩性,也把很多底层路径跑通了。

比如原生小爱同学提供了稳定的唤醒、TTS、设备控制和生态接入能力,适合普通用户日常使用。但它的对话能力、模型选择和工具扩展都比较固定,用户很难把自己的 ASR、大模型或智能家居工作流接进去。

Open-XiaoAI 这类项目则打开了更底层的设备改造路径,让音箱具备 SSH、启动脚本和自定义部署的可能。它解决的是「能不能进入设备、能不能运行自己的东西」的问题,为后续折腾提供了基础。

MiGPT 等桥接类方案则更进一步,把大模型对话接入到小爱音箱体验里,让用户可以用自然语言和 AI 聊天。这类方案的优势是思路清晰、上手相对友好,但通常需要额外服务端或桥接层来承载 Agent 逻辑,音箱本身更像前端入口。

这些方案各有价值,但也留下了一些共同痛点:语音链路复杂、部署组件偏多、原生小爱可能抢麦或抢答、云端控制可能被误触发,音频细节也容易受多层转发影响。xiaoai-agent 正是在这些探索之上,尝试把 Agent 主流程进一步收回到音箱端侧。

它解决了什么痛点

xiaoai-agent 最核心的价值,是把小爱音箱改造成一个更开放、更可控的语音 Agent 载体。

首先,它避免了「两个助手同时工作」的混乱。项目会让原生小爱同学的麦克风输入静音,由 xiaoai-agent 接管真实麦克风音频。这样可以减少抢麦、抢答,以及误触发小米云端控制的问题。

其次,它降低了长期运行的复杂度。传统方案常常需要在局域网里额外跑一个服务,音箱和服务之间再做消息转发。xiaoai-agent 选择直接在音箱端侧运行 Agent,部署链路更短,也更像一台独立设备。

第三,它复用了音箱原本已经做好的音频能力。唤醒、VPM 音频回调、TTS 播报、播放控制等能力都来自设备原生系统,日常交互会更接近「真正的音箱体验」,而不是把音箱当作一个简单的外设。

最后,它给用户留下了足够多的选择空间。ASR 可以使用兼容 OpenAI POST /v1/audio/transcriptions 的服务,大模型也可以接入 OpenAI-compatible API。你可以按自己的预算、延迟和隐私偏好选择服务,而不是被固定在单一路径里。

主要特性

xiaoai-agent 是一个非官方技术研究项目,核心 Agent 使用 Rust 编写,运行在小爱音箱端侧。目前项目已经在 Xiaomi 智能音箱 Pro(OH2P)固件 1.62.2 上测试成功。

它目前已经具备一套完整的语音 Agent 闭环:

  • 端侧常驻运行:Agent 直接运行在音箱上,不需要额外部署专门服务端。
  • 完整语音流程:支持唤醒、录音、ASR、大模型对话、工具调用和 TTS 回复。
  • 更自然的交互:支持连续对话、VAD、中途打断、回声消除和播放时录音。
  • 开放模型接入:ASR 和 LLM 均可配置为兼容 OpenAI API 的服务。
  • 工具与设备控制:内置时间、天气、音乐播放等工具,并可通过 Home Assistant MCP 控制智能家居。
  • 保留部分系统能力:语音对话由 Agent 接管,但蓝牙网关等非语音服务不应受到影响。
  • 可自定义体验:可以调整唤醒响应、LED 状态、音乐服务和运行参数。

项目仓库中也包含补丁固件制作、刷机辅助、示例配置和部署说明,方便用户从设备准备一路走到 Agent 常驻运行。需要特别说明的是,它不是官方项目,也不是面向所有型号的一键通用方案;现阶段更适合愿意折腾、理解刷机风险,并且手上有对应设备的用户尝试。

这套能力组合起来,小爱音箱就不再只是一个固定功能的智能音箱,而更像是家里可以听、可以说、可以调用工具的 AI 节点。

阅读全文 »
0%