WWW.YOUINFO.SITE
标签聚合 双栈

/tag/双栈

LinuxDo 最新话题 · 2026-05-17 22:53:31+08:00 · tech

写在前面,全文由 ANY大善人的opus4.7主导 IPV4 NEWAPI IPV6NEW直连 佬友们好。分享一下我这边的家宽双栈对外方案,脱敏整理出来给有同样需求的同学参考。 我家这边情况大概是: 广东移动运营商给了公网 IPv6,但 IPv4 是大内网 NAT1,没固定公网 IPv4 想把家里 5 个 Web 服务(api / cpa / yx / code / claw)和 1 个 VPN 挂到公网 要兼顾 公司 IPv4 (办公网、IPv4-only 环境)和 IPv6 用户 不想为这事单租 VPS 折腾完之后稳定跑了一阵,整理出来发上来。所有真实域名、公网 IP、动态端口、密钥都用 <...> 占位符替换了,自己用的时候换成真值即可。 整套方案的免费成本:Cloudflare DNS + 307 Redirect Rule 在免费计划够用,Lucky 是开源的。零 VPS 开支。 一、整体思路 核心三招: 双域名分离 :入口域名(橙云)和落地域名(灰云)分开。入口只做 307 重定向,不直连后端;落地域名灰云直接 DNS 解析到家里。 双栈分流 :IPv4 客户端被 307 重定向到 service.stun.<ROOT_DOMAIN>:<STUN_TCP4_PORT> (家宽 IPv4 不能直 443,只能走 Lucky STUN 拿到的动态端口);IPv6 客户端被 307 重定向到 service.stun.<ROOT_DOMAIN> (默认 443,直连 OpenWrt 公网 IPv6)。 VPN 独立 :VPN 不走 307,因为 WireGuard 不理解 HTTP 重定向。VPN 用独立的灰云 AAAA 直连。 二、主链路图 ┌────────────────────────────────────┐ │ Client / Browser / App │ │ service.<ROOT_DOMAIN> │ └──────────────────┬─────────────────┘ │ ┌──────────────────▼─────────────────┐ │ Cloudflare │ │ proxied entry records │ │ Dynamic Redirect, HTTP 307 │ └──────────────────┬─────────────────┘ │ ┌────────────────────────────────────────────────┼────────────────────────────────────────────────┐ │ │ │ ┌───────▼────────────────────┐ ┌──────────▼──────────────────┐ ┌──────────▼──────────────────┐ │ IPv4 client │ │ IPv6 client │ │ VPN client │ │ target should include port │ │ target uses default 443 │ │ no HTTP redirect │ └───────┬────────────────────┘ └──────────┬──────────────────┘ └──────────┬──────────────────┘ │ 307 Location: │ 307 Location: │ │ https://service.stun.<ROOT_DOMAIN>:<STUN_PORT>/path │ https://service.stun.<ROOT_DOMAIN>/path │ │ │ │ ┌───────▼────────────────────┐ ┌──────────▼──────────────────┐ ┌──────────▼──────────────────┐ │ DNS-only A │ │ DNS-only AAAA │ │ DNS-only AAAA │ │ service.stun.<ROOT_DOMAIN> │ │ service.stun.<ROOT_DOMAIN> │ │ vpn.<ROOT_DOMAIN> │ │ -> STUN public IPv4 │ │ -> OpenWrt public IPv6 │ │ -> OpenWrt public IPv6 │ └───────┬────────────────────┘ └──────────┬──────────────────┘ └──────────┬──────────────────┘ │ │ │ ┌───────▼────────────────────┐ ┌──────────▼──────────────────┐ ┌──────────▼──────────────────┐ │ Lucky STUN tcp4 │ │ OpenWrt │ │ OpenWrt VPN service │ │ public IPv4:<STUN_PORT> │ │ Lucky HTTPS :443 │ │ WireGuard/OpenVPN/etc. │ │ -> Lucky HTTPS :18443 │ │ cert *.stun.<ROOT_DOMAIN> │ │ protocol-specific port │ └───────┬────────────────────┘ └──────────┬──────────────────┘ └─────────────────────────────┘ │ │ └───────────────────────────────┬────────────────┘ │ ┌──────────────▼───────────────┐ │ OpenWrt Lucky reverse proxy │ │ same Host rules on 443/18443 │ │ service.stun.<ROOT_DOMAIN> │ │ -> backend │ └──────────────┬───────────────┘ │ ┌───────────────────────────────┼───────────────────────────────┬───────────────────────────────┐ │ │ │ │ ┌───────▼────────────────────┐ ┌────────▼───────────────────┐ ┌────────▼───────────────────┐ ┌────────▼───────────────────┐ │ api.stun.<ROOT_DOMAIN> │ │ cpa.stun.<ROOT_DOMAIN> │ │ yx.stun.<ROOT_DOMAIN> │ │ code.stun.<ROOT_DOMAIN> │ │ -> <APP_NODE_A>:8881 │ │ -> <APP_NODE_A>:8317 │ │ -> <APP_NODE_A>:5001 │ │ -> <APP_NODE_B>:19080 │ │ NewAPI │ │ CPA / Proxy API │ │ YX Web │ │ code-server │ └────────────────────────────┘ └────────────────────────────┘ └────────────────────────────┘ └────────────────────────────┘ │ ┌──────────────▼───────────────┐ │ claw.stun.<ROOT_DOMAIN> │ │ -> <APP_NODE_B>:18789 │ │ OpenClaw Gateway │ └──────────────────────────────┘ 解读: 客户端先打入口域名(橙云),被 Cloudflare 307 到对应的 stun 落地域名 IPv4 客户端拿到 https://service.stun.<ROOT_DOMAIN>:<STUN_TCP4_PORT>/... ,解析到 Lucky STUN 公网 IPv4:动态端口,进 Lucky 反代 IPv6 客户端拿到 https://service.stun.<ROOT_DOMAIN>/... (443),解析到 OpenWrt 公网 IPv6,进 Lucky 反代 两条路最后都汇到 OpenWrt Lucky 反代,按 Host 把请求转给内网真正的后端服务 VPN 走自己那条路,跟 HTTP 没关系 三、DNS 怎么设 入口域名(5 个)—— 橙云 api.<ROOT_DOMAIN> A proxied=true TTL=auto cpa.<ROOT_DOMAIN> A proxied=true TTL=auto yx.<ROOT_DOMAIN> A proxied=true TTL=auto code.<ROOT_DOMAIN> A proxied=true TTL=auto claw.<ROOT_DOMAIN> A proxied=true TTL=auto 为什么橙云:必须橙云,Cloudflare 只有收到 HTTP 请求后才能执行 Redirect Rule。灰云的话客户端绕过 CF 直接打到 A 记录,Redirect Rule 根本触发不了。入口的 A content 没那么重要(反正是 CF 边缘 IP 收的),不需要指向家里。 落地域名(5 个 + 1 个 VPN)—— 灰云 api.stun.<ROOT_DOMAIN> A=<STUN_PUBLIC_IPV4> AAAA=<OPENWRT_PUBLIC_IPV6> proxied=false TTL=60 cpa.stun.<ROOT_DOMAIN> A=<STUN_PUBLIC_IPV4> AAAA=<OPENWRT_PUBLIC_IPV6> proxied=false TTL=60 yx.stun.<ROOT_DOMAIN> A=<STUN_PUBLIC_IPV4> AAAA=<OPENWRT_PUBLIC_IPV6> proxied=false TTL=60 code.stun.<ROOT_DOMAIN> A=<STUN_PUBLIC_IPV4> AAAA=<OPENWRT_PUBLIC_IPV6> proxied=false TTL=60 claw.stun.<ROOT_DOMAIN> A=<STUN_PUBLIC_IPV4> AAAA=<OPENWRT_PUBLIC_IPV6> proxied=false TTL=60 vpn.<ROOT_DOMAIN> AAAA=<OPENWRT_PUBLIC_IPV6> proxied=false TTL=60 为什么灰云 + TTL 60:307 之后客户端要直连家里 Lucky,必须灰云让 DNS 真实解析过去。TTL 60 是为了 STUN 公网地址变化时尽快收敛(用 LuckyDDNS 自动更新 DNS A 记录)。5 个落地域名的 A 都指向 Lucky STUN 探出来的公网 IPv4,AAAA 都指向 OpenWrt 公网 IPv6,这样一张通配证书就能盖住。 四、Cloudflare Redirect Rule Cloudflare 控制台 → Rules → Redirect Rules,建一个 Dynamic Redirect Ruleset,phase 是 http_request_dynamic_redirect 。整体设置: status_code: 307 preserve_query_string: true 为什么 307:307 会保留 HTTP 方法(POST/PUT/PATCH),API 调用、表单提交、code-server 的 PUT 都不会被吃掉。301/302 在某些客户端会把 POST 改成 GET,直接坑死。 为什么 preserve_query_string:不开的话 ?token=... 、 ?folder=... 这种查询参数全丢,API 和 code-server 直接报错。 下面三条规则,按顺序放。 规则 1:cpa 根路径补全 match: http.host == "cpa.<ROOT_DOMAIN>" and path == "/" target: https://cpa.<ROOT_DOMAIN>/management.html 为什么这么设:cpa 的根路径默认不会跳到管理页,先把 / 补成 /management.html ,后面通用规则继续接管把它跳到 stun 落地域名。 规则 2:IPv4 入口加 STUN 端口 match: http.host in {api.<ROOT_DOMAIN>, cpa.<ROOT_DOMAIN>, yx.<ROOT_DOMAIN>, code.<ROOT_DOMAIN>, claw.<ROOT_DOMAIN>} and ip.src in 0.0.0.0/0 target expression: wildcard_replace( http.request.full_uri, "*://*.<ROOT_DOMAIN>/*", "https://${2}.stun.<ROOT_DOMAIN>:<STUN_TCP4_PORT>/${3}" ) 为什么这么设: ip.src in 0.0.0.0/0 是 IPv4-only 匹配。家宽 IPv4 走不了 443,必须把 Lucky STUN 当前公网端口写到 Location 里。 <STUN_TCP4_PORT> 是 动态值 ,要靠脚本或 Webhook 同步过来(见第九节坑 2)。 规则 3:IPv6 入口直接 443 match: http.host in {api.<ROOT_DOMAIN>, cpa.<ROOT_DOMAIN>, yx.<ROOT_DOMAIN>, code.<ROOT_DOMAIN>, claw.<ROOT_DOMAIN>} and not ip.src in 0.0.0.0/0 target expression: wildcard_replace( http.request.full_uri, "*://*.<ROOT_DOMAIN>/*", "https://${2}.stun.<ROOT_DOMAIN>/${3}" ) 为什么这么设:IPv6 客户端可以直达家里 OpenWrt 公网 IPv6 的 443,不需要动态端口,省事。 五、Lucky 配置 STUN tcp4 类型: tcp4 Target: <OPENWRT_LAN_IP>:18443 (指向 Lucky 自己的 HTTPS 监听口,不是 443) PublicAddr (Lucky 探出来): <STUN_PUBLIC_IPV4>:<STUN_TCP4_PORT> 为什么 Target 是 18443 不是 443:家宽 IPv4/443 实测不通,IPv4 数据面只能走 Lucky 自己单独起的 HTTPS 监听口 18443。443 留给 IPv6 直连用。 端口同步:STUN 端口是运营商分配的, 动态值 。Lucky 探到新端口后必须同步到 Cloudflare Redirect Rule 的规则 2。两种方式: Lucky Webhook → 调用 Cloudflare API PATCH Ruleset 外部脚本轮询 Lucky API → PATCH Ruleset 同步完后必须用 curl -4 -I 从外部复测 Location(见第九节坑 3,API 和边缘可能短暂不一致)。 HTTPS 双监听 Lucky 反代要同时挂两个 HTTPS 监听口: 监听 1: <OPENWRT_PUBLIC_IPV6>:443 给 IPv6 客户端直连 监听 2: <OPENWRT_LAN_IP>:18443 给 STUN tcp4 把 IPv4 流量打进来 两个口共用同一组反代规则,因为 IPv4 和 IPv6 路径最后到 Lucky 时 Host 都是 *.stun.<ROOT_DOMAIN> 。 通配证书 类型: 通配证书 *.stun.<ROOT_DOMAIN> 签发方式: DNS-01 DNS Provider: Cloudflare (用 API Token,只给 Zone DNS Edit 权限) 为什么 DNS-01:HTTP-01 要 80/443 开放给 ACME,家宽 80/443 路径本来就不全通,DNS-01 不依赖入站端口,能自动续签。一张通配盖住 5 个 stun 落地域名。 反代规则 每个 stun 落地 Host 对一个内网后端: api.stun.<ROOT_DOMAIN> -> http://<APP_NODE_A>:8881 (NewAPI) cpa.stun.<ROOT_DOMAIN> -> http://<APP_NODE_A>:8317 (CPA / Proxy API) yx.stun.<ROOT_DOMAIN> -> http://<APP_NODE_A>:5001 (YX Web) code.stun.<ROOT_DOMAIN> -> http://<APP_NODE_B>:19080 (code-server) claw.stun.<ROOT_DOMAIN> -> http://<APP_NODE_B>:18789 (OpenClaw Gateway) 公共选项: 前端协议: https 后端协议: http (TLS 在 Lucky 终止,后端走明文省事,不用每个后端搞证书) WebSocket: 开 (code-server 这种长连接工具必须开) 自动反代重定向: 关 (避免后端 Location 头被二次改写,排障痛苦) 访问日志: 开 (出问题方便看) 为什么 TLS 在 Lucky 终止:证书、SNI、续签集中维护一处,后端跑明文 HTTP 反而干净。 六、认证保护 公网第一道认证放在 Lucky,不放在 Cloudflare 入口层(CF 只做 307 不挡): api.stun.<ROOT_DOMAIN> Lucky Basic Auth: 关 后端自己有登录页 (NewAPI) cpa.stun.<ROOT_DOMAIN> Lucky Basic Auth: 开 后端通过 Basic Auth 后到 CPA API yx.stun.<ROOT_DOMAIN> Lucky Basic Auth: 开 后端通过 Basic Auth 后跳 /login code.stun.<ROOT_DOMAIN> Lucky Basic Auth: 开 code-server 后端 auth=none claw.stun.<ROOT_DOMAIN> Lucky Basic Auth: 开 后端是 OpenClaw Gateway UI 为什么 api 不开:NewAPI 自带账号系统,再叠一层 Basic Auth 反而碍事。 为什么其他都开:cpa/yx 是私有管理类入口,code-server 直接是命令执行入口( 裸露公网 = 送服务器 ),claw 是网关 UI。统一拿 Lucky Basic Auth 当公网第一道挡板,简单粗暴。 为什么 code-server 设 auth=none :code-server 自带密码方案不够灵活,统一交给前置 Basic Auth 处理。 前提是 Lucky Basic Auth 必须开 ,否则 code-server 在公网完全没保护。 顺嘴提一下:我账号下还有 2 个 Cloudflare Access SaaS/OIDC App,但那是给 Lucky 自己做第三方登录用的回调端点, 不是 业务入口拦截页。业务入口前没有 CF Access。 七、VPN 为什么不能套 307 vpn.<ROOT_DOMAIN> AAAA -> <OPENWRT_PUBLIC_IPV6> 灰云 直接灰云 AAAA 指向 OpenWrt 公网 IPv6,按 VPN 自己协议端口(WireGuard / OpenVPN / IPsec)连。 为什么不能走 307:307 是 HTTP 状态码,只有浏览器/curl 会跟随 Location。WireGuard/OpenVPN 客户端根本不解析 HTTP,把 VPN 域名扔进 Cloudflare 橙云 → 客户端连不上 → 超时。VPN、SSH、其他非 HTTP 协议都不要套 Cloudflare 307。 八、验收 curl 每条命令独立可复制粘贴,把 <...> 换成真值即可。 # 入口域名是不是返回 307 curl -I https://api.<ROOT_DOMAIN>/some-path # IPv4 Location 必须带 :<STUN_TCP4_PORT> curl -4 -I https://api.<ROOT_DOMAIN>/some-path # IPv6 Location 应该不带端口 curl -6 -I https://api.<ROOT_DOMAIN>/some-path # IPv4 STUN 落地是否可用 curl -4 -k -I https://api.stun.<ROOT_DOMAIN>:<STUN_TCP4_PORT>/ # IPv6 443 落地是否可用 curl -6 -k -I https://api.stun.<ROOT_DOMAIN>/ # Lucky Host 规则是否命中 (LAN 内测) curl -k -H "Host: api.stun.<ROOT_DOMAIN>" https://<OPENWRT_LAN_IP>:18443/ # Basic Auth 是否生效 (期望 401 + WWW-Authenticate) curl -I https://code.stun.<ROOT_DOMAIN>:<STUN_TCP4_PORT>/ 九、踩过的坑 坑 1:IPv4 走不了 443 家宽 IPv4 是大内网 NAT,公网 443 不通。一开始以为 IPv4 也能走 443,折腾半天 timeout。最后老老实实用 Lucky STUN tcp4 拿一个动态公网端口,把 Cloudflare Redirect 的 Location 写成 :<STUN_TCP4_PORT> 才通。 坑 2:STUN 端口是动态的 Lucky STUN PublicAddr 的端口运营商会换。如果 Cloudflare Redirect Rule 里写死旧端口,IPv4 用户会被重定向到失效端口,表现就是"网页打不开"。必须做端口同步:Lucky Webhook 或外部脚本 PATCH Cloudflare Ruleset,同步完用 curl -4 -I 验证 Location 是否真的带新端口。 坑 3:Cloudflare API 配置和边缘行为可能短暂不一致 PATCH 完 Ruleset,Cloudflare API 返回的 target_expression 显示带端口,但边缘 HTTPS 实际返回的 Location 没带端口,IPv4 用户继续 timeout。这种漂移可能持续几分钟。 别只看 API 返回值,必须从外部 curl -4 -I 看最终 Location 才算数。 坑 4:Lucky 404 不一定是后端挂了 直接访问 Lucky 监听口或 Host 不匹配会返回 Lucky 自己的 404/Warning 页。第一反应别去重启后端,先确认 Host 头是不是命中了反代规则: curl -k -H "Host: api.stun.<ROOT_DOMAIN>" https://<OPENWRT_LAN_IP>:18443/ 加上正确 Host 才能命中规则。 坑 5:VPN 不能套 Cloudflare 307 前面讲过:VPN 客户端不理解 HTTP 重定向,必须独立灰云 AAAA 直连,按 VPN 自己协议处理。 坑 6:排障先分清 timeout / 404 / 401 timeout :网络层问题(端口没开、IPv4/443 路径、NAT 失效、防火墙) 404 :Host 没命中 Lucky 反代规则(多半是 Cloudflare Redirect 写错了 stun 域名,或者直接打了 Lucky 监听口没带 Host) 401 :规则命中了,Lucky Basic Auth 在工作,这是 正常表现 别把 401 当成"挂了"去重启服务。 十、完整脱敏抽象架构图 ┌────────────────────┐ │ client │ └─────────┬──────────┘ │ ┌─────────▼──────────┐ │ Cloudflare orange │ │ HTTP 307 only │ └─────────┬──────────┘ │ ┌───────────────────────┴───────────────────────┐ │ │ ┌─────────▼──────────┐ ┌──────────▼─────────┐ │ IPv4 path │ │ IPv6 path │ │ stun host + port │ │ stun host + 443 │ └─────────┬──────────┘ └──────────┬─────────┘ │ │ ┌─────────▼──────────┐ ┌──────────▼─────────┐ │ Lucky STUN tcp4 │ │ OpenWrt IPv6 │ │ public:<port> │ │ Lucky HTTPS 443 │ └─────────┬──────────┘ └──────────┬─────────┘ │ │ └───────────────────────┬───────────────────────┘ │ ┌─────────▼──────────┐ │ Lucky reverse proxy│ │ Host based routing │ └─────────┬──────────┘ │ ┌────────────────────────────┼────────────────────────────┐ │ │ │ ┌───────▼────────┐ ┌────────▼───────┐ ┌─────────▼──────┐ │ service A │ │ service B │ │ service C │ │ node A:port │ │ node A:port │ │ node B:port │ └────────────────┘ └────────────────┘ └────────────────┘ 写在最后 整套方案核心就这几条: 入口必须橙云,落地必须灰云 IPv4 走 STUN 端口,IPv6 直接 443,别假设 IPv4 能走 443 307 + preserve_query_string,API 和长连接才不挂 VPN 独立,别套 307 认证放在 Lucky,不放在 CF 入口 STUN 端口动态值,必须同步 + 外部复测 所有 <...> 占位符替换成真实值再用。 真实域名、公网 IP、STUN 端口、Cloudflare Zone ID、API Token、Lucky 管理凭据、Basic Auth 账号密码、TOTP 种子、origin 旁路密钥这类敏感信息 绝对不要贴论坛 ,包括截图里也要打码。 有问题评论区交流,佬友们如果有更优雅的 STUN 端口同步方案也欢迎贴出来。 2 个帖子 - 2 位参与者 阅读完整话题

linux.do · 2026-04-22 10:20:28+08:00 · tech

前言 一般家宽不给开443端口(ipv4和ipv6检测到就封),所以我用8443替代https的默认443端口来反代。 但是方案要考虑同时支持ipv4和ipv6。 本文主要利用 OpenResty将lxc容器 8443端口的ipv6流量 转发给443端口 来实现ipv6同端口访问。 环境介绍 系统:pve 路由:pve-ikuai虚拟机 网关:pve-lxc容器安装的1panel 域名证书:所有要发布到公网的web服务都是用的同一个通配符证书(比如: *.a.com ),由1panel申请、续期。 dns管理:cloudflare 前置条件,已经配置了ikuai的ipv6,内网设备能拿到公网ipv6地址(这一步网上有大把教程) 1. 双栈域名设置 使用ikuai的ddns(动态域名),将wan口的公网ip绑定到域名, 比如a.com (A记录) 使用ikuai的ddns(动态域名),将lxc的容器ipv6绑定到域名(通过终端DUID配置), 比如a.com (AAAA记录) 注意,ipv4和ipv6用的同一个域名。此时 nslookup a.com 能同时获取到ipv4和ipv6 2. ipv4设置 ikuai做端口转发,将8443转发到lxc容器的443端口(同时转发8444作为备用)。这个配置只对ipv4有效 实现ipv4外网访问,只需此步骤即可。 3. ipv6设置 lxc容器执行 1pctl listen-ip ipv6 (1panel工具指令),让1panel同时监听ipv4和ipv6 1panel-容器-设置页面:打开ipv6开关,勾选ip6tables、experimental;子网填 fd00:1234::/64 (这里有多种填法,随便填个)。lxc执行 docker network inspect bridge 能看到ipv6段就成功了 1panel-网站-你的网站(可能有多个, 如test1.a.com )-配置-其他:勾选“监听ipv6”,保存 【重点】将lxc容器 8443端口的ipv6流量 转发给443端口 利用 1Panel 预置的 OpenResty(网站服务组件,是docker 容器)作为本地透明代理,将 8443端口的 IPv6 加密流量解密后转发给443端口(后续就和普通网页分发流程一样了): 找到 OpenResty容器内 的证书位置,因为都是用的同一个通配符证书,所以随便记录一个如 /www/sites/test1.a.com/ssl/fullchain.pem 、 /www/sites/test1.a.com/ssl/privkey.pem OpenResty容器内 添加转发配置文件, /usr/local/openresty/nginx/conf/nginx.conf/v6_bridge.conf : server { listen [::]:8443 ssl; server_name _; # 匹配所有指向此容器的域名 # 引用面板自用证书 ssl_certificate /www/sites/test1.a.com/ssl/fullchain.pem; ssl_certificate_key /www/sites/test1.a.com/ssl/privkey.pem; location / { proxy_pass https://127.0.0.1:443; # 透传原始 Host(含端口)和真实 IP proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 兼容 WebSocket(保证面板 SSH 终端可用) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } OpenResty容器内 执行 openresty -t && openresty -s reload 拓扑图 【 互联网 (Internet)8443端口进 】 / \ (IPv4) / \ (IPv6) / \ +-----------------------+ +-----------------------+ | iKuai / PVE 路由层 | | IPv6 直通 (Pass-thru) | | (NAT 映射: 8443->443) | | (目标: LXC 容器 IPv6) | +-----------+-----------+ +-----------+-----------+ | | | (IPv4:443) | (IPv6:8443) | | ________________|______________________________|_________________ | v v | | +--------------------+ +-----------------------+ | | | 1Panel 面板程序 | <----- | OpenResty (Nginx) | | | | (监听 0.0.0.0:443) | proxy | (监听 [::]:8443) | | | +---------+----------+ +-----------+-----------+ | | | | | | | | [ 最终处理与响应 ] | | | | 【 LXC 容器内部环境 】 | |_________________________________________________________________| 4. 测试 把ikuai转发的8443端口关闭(开启8444作为后备,防止在外失联) 使用蜂窝网络手机访问 test1.a.com:8443 ,能打开页面即成功(通过ipv6直接指向了lxc的8443端口) 有ipv6的电脑测试: curl -6 -v https://test1.a.com:8443 1 个帖子 - 1 位参与者 阅读完整话题

linux.do · 2026-04-21 12:15:28+08:00 · tech

我在排查一个比较奇怪的旁路由延迟问题,想请教大家有没有遇到过类似情况。 一、我的目标架构 我想实现的是: RouterOS 继续做全家默认双栈主路由 IPv6 继续由 RouterOS 原生处理 AdGuardHome + MosDNS 作为统一 DNS 中枢,尽量避免 DNS 泄漏,并兼容 IPv4/IPv6 共存 Mihomo 只负责被选中设备的 IPv4 出国流量 终端设备尽量无感切换,不想手工改每台设备的网关 也就是说,我想做的是: 普通设备:正常走 ROS 指定设备:ROS 通过 policy route 把它们的 IPv4 流量旁路到 Mihomo 但 DNS 仍然由 AdGuardHome + MosDNS 统一管理 二、当前网络环境 主路由:RouterOS RB5009 LAN:192.168.5.1/24 DNS:dns-core = 192.168.5.3 跑 AdGuardHome + MosDNS Mihomo: LXC:113 / 192.168.5.2 VM:109 / 192.168.5.2 测试设备: Win11:192.168.5.249 当前旁路方式是: RouterOS 建 PROXY_V4 路由表 对 PROXY_V4_CLIENTS 中的设备,在 prerouting 用 mark-routing PROXY_V4 的默认路由 next-hop 指向 192.168.5.2 也就是典型的: 终端默认网关仍是 192.168.5.1 但被选中的 IPv4 流量被 ROS 送去旁路由 192.168.5.2 三、现象 最奇怪的是: 直接走 AdGuardHome → MosDNS 没有这个 7~9 秒延迟 终端如果直接把网关和 DNS 改成 192.168.5.2,国内网站也是秒开 只有在 ROS 用 policy route 把流量旁路到 192.168.5.2 时,国内站会出现首包延迟 例如 Win11 上执行: curl -I -L -o NUL -s -w "namelookup:%{time_namelookup} connect:%{time_connect} starttransfer:%{time_starttransfer} total:%{time_total}\n" http://www.baidu.com 结果大概是: Win11 不在 PROXY_V4 :很快 Win11 在 PROXY_V4 :starttransfer 常见 7s ~ 9.6s Win11 直接把网关改成 192.168.5.2 :又变快 所以现象可以总结成一句: Mihomo 本身不慢,DNS 本身也不是主矛盾,只有"ROS policy route 到同网段旁路由"这条链路会卡。 四、已经做过的排查 我已经排过这些方向: 更换 Mihomo DNS 上游 调整 Mihomo DNS / fake-ip / redir-host / sniff / tun / QUIC 相关项 验证 AdGuardHome / MosDNS 解析链 分别测试 LXC 版 Mihomo 和 VM 版 Mihomo 调整 PVE 宿主机 bridge/veth/offload 关闭部分 offload 后延迟有过改善,但不能根治 换成 VM 后问题依旧,不是单纯 LXC 特有问题 五、最关键抓包结论 无论在 LXC 还是 VM 中抓包,都看到类似现象: Win11 发 SYN 服务器几乎立刻回 SYN,ACK Mihomo 主机也立刻把 SYN,ACK 发回 Win11 但 Win11 往往要过 约 7 秒 才真正回 ACK ACK 之后,后面的 HEAD /、HTTP/1.1 200 OK 都很快 所以我目前的判断是: 慢的不是 DNS,不是目标站响应,不是 Mihomo 出口,而是 TCP 三次握手最后一步。 更具体地说: 服务端回来的 SYN,ACK 已经到了旁路由,但客户端没有及时确认。 六、我目前对根因的怀疑 我怀疑根因在于: RouterOS 通过 policy route 把流量送到同网段的旁路由 192.168.5.2 时,形成了不对称回程。 当前结构是: Win11:192.168.5.249/24,默认网关 192.168.5.1 Mihomo:192.168.5.2/24 ROS 把 Win11 的 IPv4 流量送去 192.168.5.2 于是可能形成: 去程:Win11 → ROS → Mihomo 回程:Mihomo → Win11 也就是旁路由发现客户端跟自己同网段,就直接回客户端,不再回主路由。 我怀疑这导致了: conntrack / 回程路径不一致 或某种同网段旁路的实现细节问题 从而导致 TCP 握手最后 ACK 明显延迟 七、我接下来打算怎么验证 我现在考虑不再让 Mihomo 与客户端处于同网段,而是: PVE 第二块物理口 enp5s0 直接连 RouterOS ether7 建一条专用 transit 链路,例如: ROS:192.168.6.1/30 Mihomo VM:192.168.6.2/30 这样路径强制变成: 去程:Win11 → ROS → Mihomo 回程:Mihomo → ROS → Win11 也就是把潜在的不对称回程排掉。 八、想请教大家的问题 有没有人遇到过: RouterOS mark-routing + 同网段旁路 next-hop 导致国内站 TCP 首包延迟 7~9 秒 但终端直接把网关改成旁路由后又恢复正常 这种现象是否更像: 同网段旁路不对称回程 conntrack 问题 rp-filter 问题 还是 RouterOS policy routing 的某种限制/坑 对我这种目标架构: ROS 继续做双栈主网关 DNS 继续统一由 AdGuardHome + MosDNS 管 Mihomo 仅处理被选中的 IPv4 是不是更推荐: 不要让旁路由与客户端同网段 而是给 Mihomo 一条单独 transit 网段/物理链路 10 个帖子 - 3 位参与者 阅读完整话题