V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
CrazyRundong
V2EX  ›  宽带症候群

如何在网关上正确地把 DNS 请求路由到 TUN?

  •  
  •   CrazyRundong · 138 天前 · 2889 次点击
    这是一个创建于 138 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近把 OpenWRT 网关上的分流工具从 Clash 切换到了 sing-box ,顺带开始了解基于 TUN 的透明代理配置。目前遇到的一个问题是,局域网内的 DNS 请求并没有被正确地路由/转发进 sing-box 的 DNS 模块。

    具体的情况有点复杂。因为需要让家里运行着 PT 的 NAS 这类的设备不被透明代理,所以在配置 TUN 时并没有使用 auto route,而是自己编写了 nftables 规则和对应的 ip rule/ip route 策略路由,将需要代理的设备的流量标上一个特定的 fwmark ,再将标记的流量策略路由到 sing-box 的 TUN 网卡 tun0 。简化后的设置类似这样:

    # 在 nftables 的 prerouting 阶段标记需要被代理的流量:
    type filter hook prerouting priority mangle - 1; policy accept;
    # 1. 需要直连的设备 MAC 被提前存在 direct_macs set 中,不做额外处理直接放行
    ether saddr @direct_macs counter return
    # 2. 剩余的来自 LAN 的流量均需要代理,标记为 $tun_mark (0x02)
    iifname $lan_devices counter meta mark set $tun_mark
    

    之后配置策略路由:

    # 将被标记了 tun_mark 的流量查 tun_table 表路由至 tun_dev
    ip route replace default dev ${tun_dev} table ${tun_table}
    ip rule add fwmark ${tun_mark} table ${tun_table}
    

    至此整个系统可以正常工作了。但奇怪的是,只有指定了 DNS 服务器为非网关地址 (≠ 192.168.50.1) 的局域网内 DNS 请求才会被转发进 TUN ,而默认的、DNS 服务器地址为 192.168.50.1 的 DNS 请求并没有被 sing-box 看到,而是仍由监听在网关 53 端口的 dnsmasq 处理:

    # 在局域网内的任一台被透明代理的设备上:
    nslookup google.com         # 这条查询会被 dnsmasq 接收
    nslookup google.com 1.1.1.1 # 这条查询才会被正确地路由进 sing-box 的 TUN 设备
    

    请问大家是不是我配置的策略路由在哪里出问题?多谢

    第 1 条附言  ·  138 天前

    按照 @pagxir #5, #7 的回复,将 DNS 请求 DNAT 到 TUN 对应的网段后,这些数据包就被交由 FORWARD chain 处理从而能正确转发到 TUN 设备了。具体的修改如下:

    # 1. 在 prerouting chain 标记需要被代理的 DNS 数据包
    type filter hook prerouting priority mangle - 1; policy accept;
    iifname $lan_devices meta l4proto {tcp, udp} th dport 53 counter meta mark set $dns_mark return
    
    # 我的 TUN 地址是 172.19.0.1/24; fdfe:dcba:9876::1/126
    # 为防止被路由至 INPUT chain,将地址 +1
    define tun_route_ip  = 172.19.0.2
    define tun_route_ip6 = fdfe:dcba:9876::2
    
    # 2. 在 nat chain 将被标记的数据包 DNAT 到 TUN 的网段上
    type nat hook prerouting priority dstnat - 1; policy accept;
    meta mark $dns_mark dnat ip to $tun_route_ip
    meta mark $dns_mark dnat ip6 to $tun_route_ip6 
    

    感谢各位大佬~

    第 2 条附言  ·  137 天前

    完整的 nftables 代码在 99-sing-box.nft#L65-L83,需要配合相应的服务启动停止脚本来配置策略路由。谨供遇到同样困扰的朋友们参考~

    21 条回复    2024-02-24 18:35:24 +08:00
    CrazyRundong
        1
    CrazyRundong  
    OP
       138 天前
    目前的临时解决方案是在网关 sing-box 上再开一个 TProxy 端口,借助 TProxy 可以处理 UDP 请求的特性,把来自 LAN 的 TCP/UDP 53 流量通过 nftables 规则 TProxy 到这个端口。但感觉还是搞得太繁琐了,应该没必要这么麻烦
    ```
    # 增加了一条 nftables 规则来 TProxy 来自局域网的 DNS 请求
    iifname $lan_devices meta l4proto {tcp, udp} th dport 53 counter meta mark set $tproxy_mark tproxy to :$tproxy_port
    ```
    (需要配置对应的额外策略路由)
    ```
    ip route replace local default dev lo table ${tproxy_table}
    ip rule add fwmark ${tproxy_mark} table ${tproxy_table}
    ```
    EyebrowsWhite
        2
    EyebrowsWhite  
       138 天前
    应该因为你的 dnsmasq 占用了 53 端口,`ss -ulnp | grep 53`确认一下,如果不用 dnsmasq ,就把它关了,然后 sing-box 的 DNS 监听端口改为 53
    CrazyRundong
        3
    CrazyRundong  
    OP
       138 天前
    @EyebrowsWhite 目前仍然需要 dnsmasq 监听在 53 端口来处理没被代理的设备的 DNS 请求,以及托管局域网里的一些域名映射规则。sing-box 似乎默认是没有 DNS 监听端口的,不像 Clash 。我在想能不能只开一个 TUN 、不用其他端口配置就能把 DNS 给处理好了
    lookookok
        4
    lookookok  
       138 天前
    不是太懂内核网络处理,盲猜是不是你这个策略只应用于转发数据包,而入站数据包是不是还要单独设置策略路由?

    还有个折中的办法是可在 op web 设置中,指定的 dnsmasq 作为客户端源查询端口,给这个端口出站做策略走 tun ,比再来个 TProxy 简单些。
    pagxir
        5
    pagxir  
       138 天前
    走 tun 前提需要 forward ,很明显,你这个并不需要 forward ,而是走 input 链的。所以你需要要执行以下 DNAT ,使得报文走 forward 链。
    CrazyRundong
        6
    CrazyRundong  
    OP
       138 天前
    @lookookok 我的理解是 prerouting 发生在路由之前,也就是说 prerouting mangle 里打的 fwmark 是能够同时覆盖入站和转发的,参考 https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

    @pagxir 多谢大佬提醒,具体是指在 prerouting nat 里将 DNS 包 DNAT 到 TUN 的地址吗?我试试看
    pagxir
        7
    pagxir  
       138 天前
    @CrazyRundong 假设你 tun 的 IP 是 10.111.9.11/24, 那么你可以-j DNAT --to 10.111.9.222, 那样很自然就需要 forward 到 tun 出去了。(注意不能 -j DNAT --to 10.111.9.11, 那样还是会走 input 的)
    CrazyRundong
        8
    CrazyRundong  
    OP
       138 天前
    @pagxir 哈哈理解了,解释得很清楚!刚刚还在疑惑如果 DNAT 到 TUN 地址会不会又回环到 INPUT chain, 这么一说就明白了
    kingboy9525
        9
    kingboy9525  
       138 天前
    @CrazyRundong 请问有完整的 nft 可以参考一下吗?谢谢
    Kobayashi
        11
    Kobayashi  
       137 天前
    让 sing-box DNS 监听在非 53 口,在 dnsmasq 里配置其为上游呢?
    CrazyRundong
        12
    CrazyRundong  
    OP
       136 天前
    @Kobayashi #11 应该也可以!就是不知道 sing-box 的哪种入站类型可以直接接收处理 DNS 请求
    jacky4231
        13
    jacky4231  
       105 天前
    楼主,我在 openwrt23.5 上直接安装了 sing-box 1.8.0 ,自己写了 josn ,用 tun 模式,可以连通,但是不知为何下载速度测试只有 250 左右,家里是千兆的网络。本人小白,能否将自定义 nft 规则给个完整的,并且告知如何导入?多谢了!
    CrazyRundong
        14
    CrazyRundong  
    OP
       105 天前 via iPhone
    @jacky4231 #13 在主帖的第二条附言有完整链接哈
    jacky4231
        15
    jacky4231  
       104 天前
    @CrazyRundong 感谢回复,我再试试
    jacky4231
        16
    jacky4231  
       94 天前 via Android
    大佬,能否帮忙写一下 tproxy 的 init.d 和 nft 配置文件?小白一个,tun 模式网速太低了,感激不尽,📭[email protected]
    CrazyRundong
        17
    CrazyRundong  
    OP
       94 天前
    @jacky4231 #16 我没有使用 sing-box + TProxy 的计划。但是我之前的 Clash 分流是通过 TProxy 实现的,你可以参考一下 https://github.com/lirundong/homelab-infra/blob/master/openwrt-builder/files/etc/init.d/clash.skip

    另外,我这边观察到的 sing-box TUN 的网速足够跑满千兆下行 + 200M 上行,所以你观察到的网速低不一定是 sing-box TUN 的锅
    jacky4231
        18
    jacky4231  
       65 天前
    @CrazyRundong 新年快乐!能给下客户端配置文件样本吗,是不是我的配置文件有问题?多谢!
    journalist
        19
    journalist  
       64 天前
    我的 tun 接口不是在本机应该怎样修改呢,具体来说网关是 192.168.1.1 ,tun 在 192.168.1.2
    CrazyRundong
        20
    CrazyRundong  
    OP
       64 天前   ❤️ 1
    @jacky4231 #18 sing-box 配置文件可以通过 https://github.com/lirundong/homelab-infra/blob/master/conf-gen/generate.py 脚本自动生成,其源文件是 https://github.com/lirundong/homelab-infra/blob/93352db/conf-gen/source.yaml#L532-L617

    请不要做伸手党或直接留邮箱,这对社区讨论和知识共享无益,我也没有义务帮你写配置文件
    journalist
        21
    journalist  
       62 天前
    感谢!我现在已经跑通了。
    ip route replace default via 192.168.1.2 dev br-lan table ${tun_table}

    有一个问题就是 type nat hook prerouting priority dstnat - 1; policy accept; 这里会报错 Could not process rule: Not supported 。我就把 dns dnat 去掉了,用 openwrt uci 自带的端口转发功能。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2966 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 09:18 · PVG 17:18 · LAX 02:18 · JFK 05:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.