mihomo深入研究

TPROXY 透明代理原理

什么是 TPROXY

TPROXY(Transparent Proxy)是 Linux 内核原生的透明代理技术,属于 netfilter 框架的一部分。相比传统的 REDIRECT,TPROXY 在设计上更"干净",也是对应用层最透明的方案。

核心思路

TPROXY 不是「改目标地址」,而是给内核发一条指令:

这个包别路由出去,直接交给本机的 7893 端口处理,同时保留所有原始信息。

跟 REDIRECT 的对比

REDIRECT(你之前用的方式)

客户端 → 路由器(PREROUTING)
              │
              ▼
      iptables -j REDIRECT --to-ports 7892
              │
              │ 把目标IP:端口改了
              │ (例如: youtube.com:443 → 127.0.0.1:7892)
              ▼
      mihomo 收到连接
              │
              │ 需要用 SO_ORIGINAL_DST 从内核拿回真实目标
              │ (但拿回的是 IP,不知道原始域名)
              ▼
      DNS 污染时:拿到错误 IP → 连错服务器 → 失败

本质:内核把包"篡改"了,mihomo 得费力去猜原始目标是什么。

TPROXY(现在用的方式)

客户端 → 路由器(PREROUTING)
              │
              ▼
      iptables -j TPROXY --on-port 7893 --tproxy-mark 1
              │
              │ 不打改包,只是打个标记(fwmark=1)
              │ 原始目标 IP:端口 原封不动
              ▼
      策略路由:标记 1 的包 → 路由表 100
                                │
                                ▼
                         route add local 0.0.0.0/0 dev lo
                         (所有标记包都交给本机回环接口处理)
                                │
                                ▼
      mihomo 收到连接,直接看到原始目标 IP:端口
      配合 sniffer 读取 TLS SNI → 拿到 www.youtube.com
      即使 IP 被污染,也能从域名识别出正确目标

本质:内核不篡改包,只是"拦截并本地投递",mihomo 看到的和客户端发送的完全一致。

三要素

TPROXY 需要三个东西同时配合才能工作:

1. iptables mangle 表规则

iptables -t mangle -A PREROUTING -p tcp -j TPROXY --on-port 7893 --tproxy-mark 1
  • -t mangle:mangle 表在 PREROUTING 链处理,时机比 nat 表更早
  • --on-port 7893:把匹配的包交给本机 7893 端口
  • --tproxy-mark 1:给包打上标记 1,路由系统根据这个标记做策略路由

2. 策略路由

ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

这两条的意思是:凡是打了标记 1 的包,查路由表 100。路由表 100 说:所有地址都是本机地址(local),走回环接口(lo)处理。

没有策略路由的话,内核收到一个目标为 8.8.8.8:443 的包,会试图从 WAN 口发出去,而不是交给本地进程。

3. mihomo 监听 tproxy 端口

tproxy-port: 7893

mihomo 需要用 IP_TRANSPARENT socket 选项来绑定,这要求:

  • mihomo 以 root 运行(或者有 CAP_NET_ADMIN 权限)
  • 这样才能接收目标不是本机的包

关键区别:为什么 TPROXY 不怕 DNS 污染

REDIRECT 场景

客户端 DNS → 阿里 DoH → 69.171.235.22(Facebook)  ← DNS 被污染
客户端 TCP → 69.171.235.22:443
REDIRECT → 改目标为 127.0.0.1:7892
mihomo 拿到 127.0.0.1:7892,用 SO_ORIGINAL_DST 问内核
内核返回 69.171.235.22:443
mihomo 连 69.171.235.22:443 → Facebook 不认识 youtube SNI → 断

TPROXY 场景

客户端 DNS → 阿里 DoH → 69.171.235.22(Facebook)  ← 同样污染
客户端 TCP → 69.171.235.22:443
TPROXY → 不打改包,打标记 1 → 策略路由 → 本机 7893
mihomo 直接看到 69.171.235.22:443(原始目标保留着)
mihomo sniffer 嗅探 TLS 握手 → SNI: www.youtube.com
mihomo 用域名规则 → DOMAIN-SUFFIX,youtube.com,🚀 节点选择 → 走代理
mihomo 自己重新解析 youtube.com(通过 Google DNS)→ 正确 IP
代理连到正确的 YouTube 服务器 → 成功

即使 IP 被 DNS 污染,TPROXY 保留了完整包信息,sniffer 能从 TLS 层拿到真实域名。

支持 UDP(REDIRECT 做不到的)

REDIRECT 只能劫持 TCP,因为 UDP 没有连接概念,改了目标地址就没法还原了。

TPROXY 可以劫持 UDP:

tproxy-port: 7893

iptables 加一条 UDP 规则:

iptables -t mangle -A PREROUTING -p udp -j TPROXY --on-port 7893 --tproxy-mark 1

mihomo 收到 UDP 包后,可以根据原始目标 IP 做 DNS 防污染、QUIC 代理等。

数据流完整路径

┌─────────────────────────────────────────────────────────────────┐
│                        客户端 (192.168.1.215)                    │
│  浏览器 → youtube.com:443                                      │
└──────────────────────────┬──────────────────────────────────────┘
                           │ 包: src=192.168.1.215:54321
                           │     dst=69.171.235.22:443
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                   路由器 (i br-lan 进入)                         │
│                                                                 │
│  ① PREROUTING (mangle, 最先处理)                                │
│     └─ MIHEMO 链: 来源 IP=215, TCP → TPROXY --on-port 7893    │
│        包没改,但打上了 fwmark=1                                 │
│                                                                 │
│  ② 路由决策                                                    │
│     └─ fwmark=1 → 查路由表 100                                 │
│        路由表 100: local 0.0.0.0/0 dev lo                       │
│        → "这是本机地址,从 lo 送本地进程"                        │
│                                                                 │
│  ③ mihomo (监听 0.0.0.0:7893, IP_TRANSPARENT)                  │
│     收到包,看到原始目标 69.171.235.22:443                       │
│     └─ sniffer 嗅探 TLS ClientHello → SNI: www.youtube.com     │
│     └─ match DOMAIN-SUFFIX,youtube.com → 🚀 节点选择            │
│     └─ 连接 35.78.84.79:10541 (VLESS Reality) 走代理           │
│                                                                 │
│  ④ 出口 (FORWARD → POSTROUTING → pppoe-wan)                    │
│     代理流量正常路由                                              │
└─────────────────────────────────────────────────────────────────┘

总结

特性 REDIRECT TPROXY TUN
原理 改目标地址 标记 + 策略路由 虚拟网卡
TCP
UDP
原始地址 需额外获取 自动保留 自动保留
性能 略低(用户态)
配置复杂度
适合场景 简单 TCP 代理 全面透明代理 全流量接管

评论

评论功能即将上线,敬请期待。