家用宽带 IPv6 公网直连 macOS SSH 完整踩坑实录

发布于 2026-07-22

目标是手机 Termius 在外网(5G/4G 蜂窝 IPv6)直接连回家里的 Mac SSH,不用内网穿透、也不用第三方隧道。这篇记录里,排查 macOS/Termius 端只占一半,另一半、也是真正费时间的部分,是先搞清楚家里几台路由器和光猫到底卡在哪一层——写下来免得以后再走一遍弯路。

一、拓扑改造:从双重 NAT 到 IPv6 直连

1.1 起点:光猫为什么不能当主路由

光猫是电信提供的硬件,配置权限在运营商手里——路由/桥接模式切换、IPv6 前缀委派(PD)默认都是关闭的,改动大多得联系客服或者拿超级密码登录后台自己改。这台光猫年头也不短,CPU 性能一般,能撑的并发设备数量有限(NAT 转发、DHCP 这些都要吃 CPU,猜测是主要瓶颈),拿它当全屋主路由本来就不是划算的选择。

家里实际有 4 台路由器硬件:

  • 光猫:运营商提供,光纤入户的第一台设备;
  • 锐捷路由器:跑的是瘦AP(FIT AP)模式固件;
  • 两台 TP-LINK 双频千兆路由器(AC2600,TL-WDR86xx 系列):性能明显更好,能带的设备也更多。

最初的拓扑:

光猫(动态路由)→ 锐捷(主路由,WiFi + 分流)→ 两台 TP-LINK(各带一个房间)

全程动态 IP,没开 IPv6,锐捷和 TP-LINK 各做一次 NAT,双重 NAT 叠加,外面的流量根本进不来。

1.2 目标拆解:覆盖不能退步、能不买就不买、还要拿到原生 IPv6

最初的诉求说出来很简单:“手机低延迟 SSH 回家”。但真正做决策的时候,发现这背后其实同时压着三个目标,缺一个方案都不成立:

  1. 家里其他人正常用的 WiFi 覆盖和体验不能退步;
  2. 能靠现有设备解决,就不额外花钱买新硬件、也不想再折腾一次布线;
  3. 自己要拿到一条原生、低延迟的公网 IPv6 直连路径。

第一版走的是 Cloudflare 隧道方案:域名托管在 Cloudflare,配合隧道回连 Mac,完全不用动家里的路由拓扑——这个方案对目标 1、2 是最省心的。但免费账号没有 Argo 智能路由,实测流量没有走香港节点,反而被丢去了美国西海岸,RTT 大概 200ms——这个延迟对交互式 SSH(敲命令、tmux 操作)来说是能明显感觉到的卡顿,没法接受。隧道方案在目标 3 上直接不达标,只能放弃,转向原生连接。

原生连接就绕不开重新设计家里的路由拓扑。想清楚之前,其实认真考虑过干脆买一台新路由器,专门放在光猫旁边做主路由、支持 IPv6 拨号——省心,但要花钱,还要再折腾一次布线和调试,一直在“多买一台”和“想办法用现有的”之间纠结。后来把家里现有的 4 台设备(光猫、锐捷、两台 TP-LINK)的实际能力差异认真盘点了一遍,才发现根本不需要买新的——只要把设备摆到匹配它能力的角色和位置上,三个目标可以同时满足,一分钱不用多花。下面几次尝试,就是在验证“用现有设备到底行不行”。

1.3 尝试一:直接在最内层房间路由器上拨号

原来光猫到房间路由器全是动态 IP、逐级 DHCP 下发。第一反应是让离 Mac 最近的那台 TP-LINK 自己做 PPPoE 拨号,指望能顺带拿到 IPv6。拨号本身没问题,但 IPv6 完全没有出现。

查过之后搞清楚:IPv6 前缀委派(PD)是跟着真正发起 PPPoE 认证的那一跳走的,而且这台光猫的 PD 只会下发给和它直接相连的设备。中间隔着锐捷这一跳,PD 就是传不下来,跟最内层路由器自己拨不拨号没关系。

1.4 尝试二:把拨号挪到锐捷上

既然 PD 只给直连设备,那就该让直接接在光猫下面的锐捷去拨号。但锐捷这台跑的是瘦AP(FIT AP)模式——这个模式的设计定位就是靠 AC(无线控制器)通过 CAPWAP 隧道管理的从属设备,只负责转发无线帧,没有独立路由、DHCP、PPPoE 拨号的能力。别说 IPv6 了,独立拨号上网这一步都做不到。

顺带查了一下这到底是硬件限制还是固件限制:瘦AP 不能独立路由,本质上是工作模式的设计定位问题,不是芯片算力跑不动——瘦AP 天生就不是拿来当网关用的。如果刷成胖AP(标准/自治模式),理论上能具备完整路由器功能,但 IPv6 PPPoE 拨号、DHCPv6-PD 客户端这些协议栈锐捷有没有在固件里实现,取决于具体型号和固件版本,不能一概而论——手头这台没条件验证,只能算待确认项,先不指望。

这条路也走不通。

1.5 尝试三:把光猫直接改成拨号 + 分发

联系运营商把光猫本身配成路由模式拨号,指望它能把拿到的 PD 前缀再往下面分给多台设备。结果发现光猫的 PD 被锁死成“单播”——只给直连它的那一台下游设备分前缀,不会继续往下面第二跳、第三跳批量分发。锐捷、房间里的 TP-LINK 依然拿不到。

PD 只认“直连光猫的那一台”,而这台设备还得自己支持 IPv6 PPPoE 拨号 + PD 客户端——答案就很明显了:把原来两台边缘 TP-LINK 里的一台(本来在房间里带 WiFi、不需要 IPv6 的那台)搬到光猫旁边。光猫改桥接模式,这台 TP-LINK 自己拨号上网、开 IPv6 PD,LAN 侧走 SLAAC + DHCPv6,Mac 就接在这台路由器下面。

锐捷从主路由的位置上退下来,挪到不需要 IPv6、不连 Mac 的边缘位置,专职带无线设备——物尽其用,没有浪费任何一台硬件。

覆盖这条也对得上:那台被抽调去当主路由的 TP-LINK,原来所在的房间设备本来就不多,它在那边其实只是做轻量的 NAT 转发和 WiFi 接入,从没吃满过性能上限,谈不上“性能瓶颈”。腾出来的这个位置换成锐捷去顶,锐捷撑不起路由和 IPv6,但顶一个轻量级的边缘 AP 绰绰有余,覆盖和信号强度不受影响。相当于把 4 台设备按“能力匹配需求”重新排了一遍座位:光猫本身被锁死只能桥接,专门负责把光纤信号接进来;两台 TP-LINK 里性能更好、支持 IPv6 拨号的那台顶到最吃劲的位置(直连光猫,扛路由 + PD);锐捷本来就撑不起路由和 IPv6,退到需求最轻的边缘位置正合适。三个目标(覆盖不退步、不多花钱、拿到原生 IPv6)同时满足。

最终拓扑:

光猫【桥接模式】→ TP-LINK(主路由,PPPoE 拨号 + IPv6 PD)→ macOS 终端
                              └→ 锐捷(边缘 AP,仅无线,不需要 IPv6)

搞清楚这条链路之后,剩下 macOS 端和 Termius 端的排查就是常规操作了,记录如下。

二、macOS / Termius 端踩坑记录

2.1 IPv6 地址认知混淆

ifconfig 会看到两条公网 IPv6:

inet6 2001:db8:abcd:1234:10d5:ba62:47bb:dd4c secured
inet6 2001:db8:abcd:1234:7000:bcf7:361d:349d temporary

一开始不清楚该用哪一条。

  • secured:基于网卡 MAC 生成的稳定 EUI64 地址,适合入站 SSH;
  • temporary:隐私临时地址(RFC 8981),系统会周期性自动轮换,绝对不能用于对外服务
  • fe80:: 链路本地地址:仅局域网可用,外网无法路由。

固定选用标记 secured 的全局单播地址做 SSH 访问地址。

2.2 方括号 [] 的两个坑,一起记

nc -6zv [2001:db8:abcd:1234::xxxx] 22
zsh: no matches found: [2001:db8:abcd:1234::xxxx]
  • zsh 语法层:zsh 默认把 [] 当 glob 通配符,不会原样传给 nc/sshnc 测试直接裸写 IPv6,不要加方括号;ssh 可以裸写(ssh alanlee@2001:db8:...),或者用单引号整体包裹 RFC 标准格式(ssh 'alanlee@[2001:db8:...]')。
  • nc 工具层:就算引号处理对了,macOS 自带的 BSD netcat 依然不支持方括号包裹地址语法——方括号规范只用于 URL、SSH 客户端、浏览器,不适用于系统 nc 工具,硬加会报 getaddrinfo: nodename nor servname provided。本地连通性测试一律去掉 [],直接用原始地址。

重要区分:终端命令行规则 ≠ Termius 客户端规则。Termius 地址栏必须填写带方括号格式 [IPv6 地址],遵循 RFC 3986 标准。

2.3 本地 nc 测试成功,但外网手机连不上

Mac 本地 nc 测试 Connection succeeded!,局域网 IPv6 SSH 也正常;手机切到蜂窝网络后 Termius 连接超时。结合上一节搞清楚的拓扑,问题其实是这几层叠在一起:

  1. 旧拓扑本身(头号杀手):锐捷(瘦AP模式)当主路由、双重 NAT,外网流量根本进不到内网;
  2. 光猫的 IPv6 PD 只下发给直连设备,隔了一跳的锐捷、房间路由器永远拿不到公网 IPv6;
  3. 路由器自身的 IPv6 防火墙默认拦截入站 TCP;
  4. 部分宽带运营商额外封禁 22 端口的 IPv6 入站;
  5. 手机 Wi-Fi 若没有 IPv6 双栈,无法发起 IPv6 连接,必须切 5G/4G 蜂窝测试。

最终方案见上一节:把一台边缘 TP-LINK 提拔为直连光猫的主路由,由它拨号并拿到 IPv6 PD,再配置 IPv6 防火墙放行 SSH 端口。

2.4 地址会动态变化

PPPoE 断线重拨后,运营商分配的 /64 前缀会整体变更,之前记下的所有 IPv6 地址随之失效——家用宽带几乎没有静态 IPv6,前缀是动态分配的。长期方案是部署 IPv6 DDNS(Cloudflare AAAA 记录),用定时脚本上报 Mac 当前的 secured 地址,Termius 改用域名连接,不用手动更新 IP。

三、完整分步实施方案

步骤 1:光猫设置(关键前置)

  1. 用超级管理员账号登录光猫后台;
  2. 将宽带连接由【路由模式】改为【桥接模式】,关闭光猫自带的 DHCP、IPv6 路由功能;
  3. 保存重启光猫。

没有超级账号的话,联系运营商客服远程改桥接。

这台 TP-LINK 现在直连光猫、承担原来锐捷的主路由角色:

  1. WAN 上网方式:PPPoE 拨号,填宽带账号密码;
  2. 进入【高级 → IPv6】:
    • 开启 IPv6 功能;
    • IPv6 连接类型:PPPoE,勾选复用 IPv4 PPPoE 会话
    • 高级设置里启用 Prefix Delegation(前缀委派/PD)
    • LAN 侧 IPv6 模式选 SLAAC + DHCPv6,让内网终端自动获取公网 IPv6;
  3. IPv6 防火墙配置(重中之重),添加入站放行规则:
    • 协议:TCP
    • 目的端口:22(SSH)
    • 目的网段:分配给内网的 /64 前缀
    • 源地址:按需限制,不建议永久对任意来源开放
  4. 锐捷降级为下游边缘 AP,接在这台 TP-LINK 后面,只负责无线覆盖,不承担拨号和 IPv6 职责。

安全建议:尽量限制源地址范围,而不是对全网永久开放 22 端口;也可以考虑修改 SSH 默认端口来规避运营商的端口封禁。

步骤 3:macOS 终端配置

  1. 系统设置 → 通用 → 共享 → 打开【远程登录(SSH)】;
  2. 确认 sshd 监听 IPv6(Mac 默认一般已开启):
sudo lsof -i6 :22
# 出现 [::]:ssh 代表监听全部 IPv6 地址
  1. 获取本机可用公网 IPv6:
ifconfig
# 选取带 secured 标记的地址,排除 temporary、fe80:: 地址
  1. 本地连通自测:
nc -6zv <你的 secured IPv6> 22
ssh alanlee@<你的 secured IPv6>

步骤 4:手机 Termius 连接配置

  1. 新建 Host;
  2. Address:[<你的 secured IPv6 地址>](必须带方括号);
  3. Port:22;
  4. Username:Mac 登录用户名;
  5. 认证:密码 / SSH 密钥(推荐密钥登录)。

测试时手机切换蜂窝 5G/4G 流量,不要连家里 Wi-Fi。

步骤 5:长期稳定优化(解决动态前缀)

  1. Cloudflare 域名添加 AAAA 解析记录;
  2. Mac 上写定时 shell 脚本,检测当前 secured IPv6,调用 Cloudflare API 自动更新域名记录;
  3. Termius 改用域名连接,不再填写原始 IPv6 地址。

步骤 6:排错自检清单

  1. 光猫是否桥接?路由模式会导致外网无法入站;
  2. 承担拨号的这台路由器是否直连光猫?隔了一跳的设备永远拿不到 PD 前缀;
  3. 这台路由器是否开启了 IPv6 PD 前缀委派?
  4. 路由器 IPv6 防火墙是否放行了 SSH 端口?
  5. Mac 防火墙是否允许远程登录的传入连接?
  6. 测试手机是否具备 IPv6(优先用蜂窝流量)?
  7. 是否选错了 temporary 临时地址?

小结

这次排查其实分两层:上层是家里的拓扑和运营商策略——双重 NAT、光猫 PD 只认直连设备、瘦AP 没有独立路由能力,这几条不搞清楚,下面怎么调 macOS 和 Termius 都是白费。下层才是“分层验证”的常规活:本地能通不代表外网能通,中间隔着路由器的 IPv6 防火墙、运营商的端口策略、手机网络的 IPv6 支持,任何一层出问题表现都是“连不上”。真正有效的方法是把整条链路从光猫到手机逐段验证,而不是在某一层反复猜。