透明代理让应用不必显式使用HTTP或SOCKS代理接口,由主机或网关接管选定流量,再按策略转发。它并不意味着应用绝对无法观察差异,也不等于透明解密:连接时序、地址、协议支持和证书边界仍可能改变。
需要分开回答:包怎样到代理,代理怎样获得原始目标,如何推断域名,以及代理自己的出站怎样避免再次被接管。只配置一个监听端口或一条默认路由,通常不足以完成整个闭环。
需求 关键机制
接收本机应用出站 本机OUTPUT路径、路由或TUN接管
接收其他机器经网关转发的流量 入站PREROUTING及路由交付
保留报文原目的地址 TPROXY等透明socket机制
获取应用请求的域名 DNS映射、可见握手信息或应用元数据
防止代理递归接管自己 出站标记、路由规则或隔离边界
同时覆盖IPv4/IPv6 两个地址族的路由、规则和DNS行为
三种接管方式不在同一抽象层
接管流量和识别目标是两项不同工作
REDIRECT:改写目的端点
NAT / conntrack;原目标恢复依赖协议与实现
TPROXY:保留报文目的地址
透明 socket + 匹配规则 + 策略路由共同配合
TUN:把 IP 包送到用户态程序
程序再转发 IP,或终止连接并转换为代理协议
三者都不自动赋予域名信息,也不自动解密 HTTPS;还需独立的域名与信任策略。
REDIRECT:修改目的端点,借助连接跟踪恢复关系
REDIRECT是特殊的目的NAT。IPv4本机OUTPUT场景可重定向到回环地址,PREROUTING场景通常选择入接口本地地址;不能把全部情况写成改到127.0.0.1。iptables REDIRECT
NAT改了当前报文的目的地址,但conntrack可以保留原始方向的元组。TCP代理可通过适用的SO_ORIGINAL_DST查询原目的;这不是只在REDIRECT目标下才有意义的专用接口,也不应该简单写成“原地址永久丢失”。
Linux 7.2源码中,net/netfilter/nf_nat_redirect.c区分LOCAL_OUT与PRE_ROUTING地址选择;nf_conntrack_proto.c的getorigdst查询conntrack原始目的,并限制相应协议。UDP不能把这个TCP接口当作通用恢复方法,数据报原目标获取需单独设计。
REDIRECT让普通目的地址变成本地地址,但代理仍需理解被转发流量的协议和目标。拿TLS SNI代替原目标也不完整:SNI没有目的端口,也不保证每个连接都有可见域名。
TPROXY:包不做目的 NAT,仍要交给本地透明 socket
TPROXY将匹配流量交付到支持透明接收的socket,保留IP/传输层目的端点。其有效工作需要透明监听、规则选择、标记及策略路由共同配合;应用需按要求设置IP_TRANSPARENT等选项。
例如规则把选定包标记为1,策略规则查表100,而表100含local类型路由,就可以把非本机目的地址交给本地协议栈。local ... dev lo在这里表达本地交付,不是把原目的IP改写成127.0.0.1。
只有TPROXY动作没有对应路由,或者只建路由而代理不支持透明socket,都不能组成完整路径。本机生成流量与网关入站走不同hook,也不能认为一条PREROUTING规则同时接管所有本机出站。Linux TPROXY文档
TCP连接的目标可以从适用socket语义获取,UDP通常需按每个数据报处理目的元信息。实际程序仍需正确处理地址族、端口与回包来源,而非假设一条UDP监听socket只有一个永久目标。
TUN:用户态收发 IP 包的设备接口
TUN面向IP包,TAP面向以太网帧。Linux程序通过/dev/net/tun等接口创建或附着设备后,从文件描述符读取内核发向该设备的数据,再把返回数据写入内核接收路径。是否有4 B packet information前缀取决于IFF_NO_PI等设置,不能对所有实现固定跳过同一长度。
TUN本身不进行加密、不解析域名,也不自动提供SOCKS出口。用户态程序可以把IP包封装到VPN,也可以运行协议栈,终止本地TCP并向代理建立另一条连接。后者不等于把原TCP段逐个原样送进CONNECT。Linux TUN/TAP
虚拟网卡不一定都是/dev/net/tun。例如Linux内核WireGuard使用自身网络设备实现,用户态版本才可能通过TUN交付IP包;不能把所有WireGuard和TUN代理画成完全相同的数据路径。
出站绕行:DIRECT 同样会遇到循环
代理自己的出站连接必须有绕开接管路径的出口
应用发包
路由选择 TUN
用户态代理
读取 IP 包,选择转发策略
代理出站 socket
标记 / 接口 / 路由隔离
物理出口
直连目标或连接代理节点
若出站再次走默认 TUN,会形成递归接管;DIRECT 也必须真正绕开 TUN。
应用的目标包被路由到TUN,代理决定DIRECT后仍需创建真实出站连接。若新socket继续命中同一默认路由,连接又回到TUN,形成递归。连接远端代理节点也一样,不能只排除DIRECT目标而忘记节点地址。
可采用策略mark、专用路由表、绑定接口或网络命名空间等方式;具体优先级和地址覆盖必须配套。按进程UID绕过也有作用域与共享用户的问题,不能假设任意平台都具备相同规则语义。
以下只模拟单一IPv4教学策略:代理出站mark优先绕行,明确的本地网段绕行,其余才交给TUN。它不修改真实路由,也不覆盖所有特殊地址或部署规则。
from ipaddress import ip_address , ip_network
BYPASS_MARK = 0x 80
local_ranges = [ ip_network ( ' 127.0.0.0/8' ) , ip_network ( ' 192.168.0.0/16' ) ]
def route ( destination , mark = 0 ) :
addr = ip_address ( destination )
if mark & BYPASS_MARK :
return ' physical'
if any ( addr in net for net in local_ranges ) :
return ' local'
return ' tun'
assert route ( ' 203.0.113.10' ) == ' tun'
assert route ( ' 203.0.113.10' , BYPASS_MARK ) == ' physical'
assert route ( ' 192.168.1.10' ) == ' local'
assert route ( ' 198.18.0.42' ) == ' tun'
assert route ( ' 192.0.2.20' ) == ' tun'
assert route ( ' 192.0.2.20' , BYPASS_MARK ) == ' physical'
print ( ' 本地绕行、代理出口与递归风险模型通过' )
“创建TUN不必用iptables”不代表部署不需要防火墙或策略路由。Mihomo的auto-route、auto-redirect、strict-route等选项有平台和实现限制,应阅读当前版本配置,而不是把名字解释成跨平台固定行为。Mihomo TUN配置
原始目的 IP 不等于域名
同一IP可以服务很多域名,同一域名也可对应多个地址。IP归属或地理数据库不能可靠恢复应用想访问的名字;“IP落在某国家”也不是域名分类的等价替代。
信息来源 能提供什么 缺失或不可靠的情况
原始IP/端口 网络目标 不含服务域名
明文HTTP Host 应用声明的HTTP目标 HTTPS下不可直接读取,也可能与连接目标不一致
TLS ClientHello SNI 可见的服务名称提示 无SNI、ECH或解析材料不全
DNS关联表 某次解析与后续连接关联 共享地址、缓存、旁路解析及生命周期差异
Fake-IP映射 本地合成地址所代表的名称 映射丢失、冲突或客户端绕过该DNS
TLS ClientHello不是在固定的“5+32”偏移就能找到SNI:记录、握手长度、session ID、套件和扩展均需按结构解析,还可能跨TCP读取边界。QUIC Initial则还需去除相应保护并重组CRYPTO数据,不能直接对UDP payload做固定偏移查找。
ECH隐藏内层ClientHello,外层公开名字不一定是应用原始目标。嗅探可用于有限的路由提示,不应当作可靠身份认证。RFC 9849:ECH
Fake-IP:分配本地索引,不是把域名编码进 IP
代理控制的DNS可给api.example.com分配198.18.0.42,客户端随后连接该合成地址;接管程序查本地映射,恢复域名并决定实际出口。地址本身通常无法单独解码出域名,关键状态在映射表中。
常见池使用198.18.0.0/15,但该段的IANA用途是网络基准测试,不是全球统一专为代理预留的地址。部署需避免与实际网络使用冲突,也不能让合成地址无映射地流到公网。IANA特殊地址登记
例如客户端缓存合成地址10分钟,代理1分钟后重启并把同一地址重分配给另一域名,就可能误路由。映射持久化、地址回收、DNS TTL和活动连接生命周期需协调;只保证当前表里“一IP一域名”还不够。
若客户端通过自带DoH获得真实IP,连接仍可被TUN接管,但Fake-IP映射可能不存在;这是“流量已接管、域名信息不足”,不一定是“流量完全绕过代理”。相反,只接管UDP53也不覆盖TCP DNS、DoT或DoH。对A与AAAA、直连域名及Fake-IP过滤应有一致策略。Mihomo DNS配置
映射清空后,缓存里的IP还代表原域名吗?
先清空映射再连接,地址本身不能恢复名称。接着误复用该地址,客户端依然想访问api.example,代理却会查出other.example。修复需要协调双方状态,而不是只让当前映射表看起来一一对应。
正在呈现知识画面 重新加载
映射清空后,缓存里的IP还代表原域名吗? 合成IP是本地映射的索引。客户端副本与代理映射生命周期不同,过早复用地址会把旧意图指向新对象。
{"id":"fake-ip-lifetime","title":"映射清空后,缓存里的IP还代表原域名吗?","summary":"合成IP是本地映射的索引。客户端副本与代理映射生命周期不同,过早复用地址会把旧意图指向新对象。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"clear\" data-i18n=\"clear\"></button><button id=\"reuse\" data-i18n=\"reuse\"></button><button id=\"refresh\" data-i18n=\"refresh\"></button><button id=\"connect\" data-i18n=\"connect\"></button><button id=\"reset\" data-i18n=\"reset\"></button></div><section class=\"panel\"><h3 data-i18n=\"state\"></h3><div id=\"state\"></div></section><section class=\"panel\"><h3 data-i18n=\"last\"></h3><div id=\"last\"></div></section><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n#state .tr{grid-template-columns:1fr 1.8fr}.tr>*{min-height:5em}#last{min-height:5.4em;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}let map='api.example',last='∅';function draw(){table('state',[['client intent','api.example'],['client cache','198.18.0.42'],['proxy map',map||'∅']]);set('last',last);$('#state').dataset.map=map||'';}$('#clear').onclick=()=>{map=null;draw()};$('#reuse').onclick=()=>{map='other.example';draw()};$('#refresh').onclick=()=>{map='api.example';draw()};$('#connect').onclick=()=>{last=!map?'FAIL: no mapping':map==='api.example'?'api.example ✓':'WRONG TARGET: '+map;draw()};$('#reset').onclick=()=>{map='api.example';last='∅';draw()};draw();","audio":false,"strings":{"clear":"清空代理映射","connect":"使用缓存IP连接","heading":"映射清空后,缓存里的IP还代表原域名吗?","intro":"保留客户端缓存,单独清空或重新分配代理映射,再尝试连接。","last":"上次连接结果","refresh":"重新解析并恢复映射","reset":"重置","reuse":"误复用给 other.example","scope":"固定198.18.0.42,客户端缓存api.example;代理可丢映射或误复用给other.example。演示错误与恢复,不实施网络或推荐地址池配置。","scopeLabel":"模型范围与约定","state":"两侧状态"}}
MTU、地址族与故障定位
接管后增加封装可能降低有效MTU;小请求成功、大传输停顿时,应检查路径MTU、ICMP错误传播和MSS等证据。用户态TCP栈、UDP中继与IP封装路径的处理方式不同,不能统一用一个“改MTU为固定值”解决所有问题。
IPv4默认路由0.0.0.0/0不覆盖IPv6。若AAAA可用而IPv6路径未接管,应用可能选择IPv6出口;若一律阻断IPv6,又可能改变可达性和地址选择。应根据预期覆盖范围明确配置,而不是把能访问某个IPv4站点当完整验收。
只读检查可以按次序查看当前规则与路由,再与代理日志对应:
ip rule show
ip - 6 rule show
ip route show table all
ip - 6 route show table all
ip - d link show
观察结果 下一步定位
代理没有收到目标流量 地址族、路由、hook与匹配条件
收到流量但原目标不对 NAT恢复、透明socket、UDP元信息
能识别目标但反复出现相同出站 出站绕行与mark/rule优先级
只有某些域名分流错误 DNS路径、映射、ECH与嗅探限制
只有UDP失败 代理出口能力、关联与MTU
只有大响应或长连接异常 分片/PMTUD、背压、状态超时
这些检查用于理解现有部署;配置时需把监听、接管、绕行和返回路径作为完整方案核对。
继续查阅