本页目录
透明代理与 TUN
不需要客户端配置的代理——在网关处静默劫持流量,对用户完全透明。核心挑战是分流:从 ClientHello 的 SNI 或 fake-ip DNS 判断"这个连接去哪儿",TPROXY 和 TUN 是两种劫持实现,各有利弊。
概述
透明代理不需要客户端配置——在网关处静默劫持流量,对用户完全透明。实现方式从 nftables REDIRECT 到 TPROXY 到 TUN 虚拟网卡。核心挑战是分流:代理如何知道一个连接去的是 baidu.com 还是 google.com?TLS SNI sniffing 从 ClientHello 提取域名,fake-ip DNS 则更彻底——将域名编码为假 IP,连接时反向查表。
透明代理解决的问题
传统代理需要 client 显式配置代理地址 (浏览器设置 / 系统 proxy / 应用层 proxy)。透明代理消除这个要求: 在网关处无声劫持流量, client 完全不知道代理存在。
三种实现方式
REDIRECT (iptables NAT)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 7891
原理: DNAT — 改写目标 IP:port 为 127.0.0.1:7891
问题: 代理不知道原始目标地址 (原本的 dst IP:port 丢失了)
→ 代理需要:
1. getsockopt(SO_ORIGINAL_DST) — 只对 REDIRECT 有效 (conntrack 记录了原始目标)
2. sniffing — 从 TLS ClientHello SNI 或 HTTP Host header 提取域名
TPROXY
iptables -t mangle -A PREROUTING -p tcp -j TPROXY --on-port 7891 --tproxy-mark 0x1/0x1
ip rule add fwmark 0x1 table 100
ip route add local default dev lo table 100
TPROXY 与 REDIRECT 的区别:
REDIRECT: 改 dst IP:port → 丢失原始目标
TPROXY: 不改 dst IP:port → 保留原始目标地址
→ 代理: 原本的 dst IP:port 还在 → 直接读 socket 的原本 destination → 不需要 SO_ORIGINAL_DST
→ 可以代理非本机的流量 (作为真正的网关透明代理)
TPROXY mark: 标记包 → policy routing (table 100) → 路由到本地 (即使 dst 不是本机)
原理: "local default dev lo" → 强制交给本地协议栈处理 → 代理接收包
TUN device
TUN 不需要 iptables—它创建一个虚拟 NIC,把流量在 IP 层拦截:
[TUN interface: utun / tun0] ← mihomo / WireGuard / any VPN
↑ 路由: 0.0.0.0/0 → tun0 (所有流量走 TUN)
应用 → 路由 → tun0 → mihomo 协议栈处理 → 分流:
→ DIRECT: 重新路由 (绕过 tun0) → 物理网卡出去
→ PROXY: 加密 + 封装 → 物理网卡 → 代理节点
nftables 辅助 (在 tun0 出口处):
table inet mihomo {
chain prerouting {
type filter hook prerouting priority -150; policy accept;
ip daddr 192.168.0.0/16 return ← 局域网不劫持
meta mark 0x1 return ← 已标记的不再劫持
}
}
域名分流: Sniffer + Fake-IP
代理需要知道"这个连接是去 baidu.com 还是 google.com"来决策 DIRECT vs PROXY。两种方式获取域名:
Sniffer (被动嗅探)
TLS ClientHello (明文, 在 Encrypted ClientHello 普遍部署前):
偏移 5 + 32 bytes (random) + session_id + cipher_suites
→ SNI Extension (type=0x0000) → hostname
→ mihomo: 在收到 ClientHello 时, peek 前几个 bytes → 提取 SNI → 分流决策
HTTP:
GET http://example.com/path HTTP/1.1
Host: example.com
→ 直接读 Host header
QUIC:
QUIC Initial packet 的 SNI (在 TLS ClientHello 中, 但 QUIC 用 CRYPTO frames)
→ 类似 TLS, 但需要解析 QUIC frame wrapping
Fake-IP DNS
普通 DNS: client 获得到 baidu.com → 123.125.115.110 → TCP 连接时只看到 IP → 代理必须用 IP 归属 (GEOIP) 猜测域名。
Fake-IP: DNS 返回假的 198.18.0.0/15 地址,每个 IP 对应唯一域名:
Client: DNS query google.com
mihomo (fake-ip DNS): 返回 198.18.0.42 (不是真实 IP)
Client: TCP connect 198.18.0.42:443
mihomo TUN: 收到 SYN → 查 fake-ip table → 198.18.0.42 = google.com → 分流决策
GEOIP 退化为 fallback。域名分流依赖 fake-ip 的准确性。
参考
- mihomo: wiki.metacubex.one/config/dns
- TPROXY: kernel Documentation/networking/tproxy.txt
- nftables: wiki.nftables.org
Keywords: transparent proxy, TPROXY, TUN, sniffer, fake-ip, nftables, policy routing