网页能完成 TCP 握手,却在传输较大响应时停住,可能是路径大小反馈出了问题:握手小包通过,数据包超过某一出口 MTU,返回的 ICMP 又未到达发送端。但这个现象也可能来自过滤、拥塞或服务端停顿。ICMP 的价值是提供可关联到原始报文的反馈,让诊断从“猜原因”走向“核对证据”。
想确认什么 应观察什么 单独不足以证明什么
IP Echo 往返是否可用 匹配的 Echo Reply 与 RTT TCP 服务、DNS 或所有包长都正常
哪些节点参与探测路径 TTL/Hop Limit 到期反馈 每一跳都必须响应、返回路径相同
是否有 MTU 限制 对应原包的 Too Big/Frag Needed 一次超时就是 MTU 黑洞
发送端是否采纳反馈 后续包长、重传区间和路径估计 抓到 ICMP 就等于应用已恢复
两个协议族,不能混用类型编号
ICMPv4 在 IPv4 中的 Protocol 为 1;ICMPv6 的 Next Header 为 58。它们服务于 IP 的错误报告与信息交换,不提供通用可靠字节流。Echo 可以携带数据,但这不使 ICMP 成为 TCP 那样的应用传输服务。
用途 ICMPv4 Type/Code ICMPv6 Type/Code
Echo 请求 / 回复 8/0、0/0 128/0、129/0
端口不可达 3/3 1/4
包过大 3/4,需要分片且 DF 已置位 2/0,Packet Too Big
转发寿命耗尽 11/0 3/0
分片重组超时 11/1 3/1
参数问题 Type 12,具体看 Code Type 4,具体看 Code
这是一张常用子集表,不能据此把所有不可达归为同一个错误。ICMPv6 还承载邻居与路由器发现等消息,粗暴屏蔽整个协议会影响基本网络运行。RFC 792:ICMPv4 、RFC 4443:ICMPv6
错误消息为什么能找到原连接
ICMP 通用起始部分是 Type、Code、Checksum,后面结构随类型变化。错误消息引用触发错误的原始 IP 报文,端点结合地址、协议及可用的端口/序号等,把反馈关联到对应传输或探测。
以 IPv4 Fragmentation Needed 为例,前 8 B ICMP 头中包含 16 bit Next-Hop MTU,后接原 IP 头及原始数据起始部分。经典最低引用范围包含原 IP 头和最初 8 B 数据,但收到更多引用内容也不异常。解析引用报文时仍应按其真实 IHL、长度与可用字节检查,不能信任里面宣称的原始 Total Length 就直接越界读取。RFC 1191:MTU 字段与反馈
ICMPv4 校验覆盖 ICMP 消息;ICMPv6 校验还使用 IPv6 伪首部。校验成功只说明相应错误检测通过,不认证发送者。端点需要验证引用内容与已有发送状态相符,避免无关或伪造反馈任意改变连接状态。
错误反馈也不是可靠交付:通常不会针对另一个 ICMP 错误继续产生错误,以免递归;多播、广播、非首片等还有各协议的限制及例外,IPv6 Packet Too Big 就有重要例外。再加限速、丢包与过滤,没有反馈不等于没有错误 。RFC 4443:错误生成规则
ping:一次匹配的往返样本
Echo 的 Identifier 和 Sequence 帮助关联请求/回复,Identifier 不应固定理解为进程 PID。RTT 包含去程、对端处理和回程;不能直接除二当作单向延迟,也不能用它代表高负载下应用请求延迟。
回复 TTL=57,只有在初始 TTL 确实为 64 的前提下,才支持回程经过约七次递减的推算。初始值可配置,中间可能有隧道或地址转换,因此不能据此确定操作系统,甚至不能直接推断正向跳数。
同一 1500 B MTU:应用可用字节取决于具体头部
IPv4 Echo
IP 20 B
上层头 8 B
数据 1472 B
IPv6 Echo
IP 40 B
上层头 8 B
数据 1452 B
IPv4 TCP
IP 20 B
上层头 20 B
数据 1460 B
IPv6 TCP
IP 40 B
上层头 20 B
数据 1440 B
不含 IPv4 选项、IPv6 扩展头和 TCP 选项;框宽用于阅读,不按字节比例。
Linux iputils 的 ping -s 指 Echo 数据字节数。无选项 IPv4 的 -s 1472 对应 20+8+1472=1500 B IP 包;IPv6 无扩展头时相应值是 1452。以太网头和 FCS 不算在这个 IP MTU 中。iputils ping 手册
ping - 4 - c 3 - M do - s 1472 < 目标IPv4 >
ping - 4 - c 3 - M do - s 1464 < 目标IPv4 >
ping - 6 - c 3 - M do - s 1452 < 目标IPv6 >
-M do 受内核 PMTU 检查约束,过大的发送可能在本机直接报错,而非先到达中间路由器。需要区分本地发送失败、网络返回的错误和等待超时。三个探测样本也只是快速观察,不足以估计稳定丢包率。
traceroute:逐步延长探测寿命
从 TTL=1 开始,第一台转发路由器使其耗尽,按条件返回 Time Exceeded;增大 TTL 后,探测可到更远节点。最终如何判定到达取决于探测协议:UDP 常利用目的端口不可达,ICMP Echo 利用回复,TCP 可利用 SYN-ACK 或 RST 等结果。
每一跳显示的时间都是从探测源到该反馈节点再返回的往返时间,不能简单用相邻 RTT 相减,声称得到了两台路由器之间的链路延迟。反馈可能走不同回程,设备对 ICMP 的处理优先级也可能不同。
看到的现象 更稳妥的解释
中间一跳是 *,后续与目标仍回复 该跳未在时限内给出可见反馈,转发可能正常
中间一跳“高丢包”,终点正常 可能是反馈限速,不能直接指控其转发丢包
同一 TTL 出现多个地址 可能有 ECMP、路径变化或回复源地址差异
UDP 与 TCP 路径结果不同 五元组散列、策略与过滤可能不同
传统探测改变 UDP 目的端口时可能改变 ECMP 散列,因此连续打印的地址不一定属于一条真实固定路径。定位业务问题应尽量对齐目标、协议、端口、时间和方向,并把路径图视为观测结果。
PMTUD:完整地跟踪一次 1500 → 1492 调整
PMTUD 的反馈回到原发送端,不沿原方向继续
发送端 S
发送 1500 B,DF=1
路由器 R
出口 MTU=1492
接收端 D
本次大包未收到
丢弃
ICMP 3/4:MTU=1492 + 原包引用
后续按可用大小分包;反馈路径本身也可能被过滤
示意 IPv4 经典 PMTUD;IPv6 对应 Packet Too Big,不能通过路由器分片绕过。
假设无选项 IPv4/TCP,接收方曾在 SYN 中通告 MSS=1460。发送端发出含 1460 B TCP 数据的 1500 B IP 包,DF=1;路由器出口 MTU=1492,反馈携带 1492。发送端验证反馈并更新路径估计后,后续数据包的有效数据上限可降为 1492−20−20=1452 B。
这里变化的是发送端当前使用的分段大小 ,并非连接中途重新握手把接收方通告的 MSS 改成了 1452。若带 12 B TCP 选项,则同一 PMTU 下数据还要扣除这 12 B;IPv6 或隧道也需要按实际头部预算。
TCP 会按需要重传尚未确认的数据区间,IP 本身不保证替原包重发。路径可能再次改变,也可能还有更小的后续瓶颈,所以收到一次 1492 不代表永久找到唯一正确值。RFC 1191
IPv6 对应 Packet Too Big,基本头无 DF 位,路由器不分片;扩展头也占空间,不能因为基本头固定就忽略它们。经典 PMTUD 需要接收可信的大小反馈,具体缓存对象、寿命与重新探测行为由实现和配置决定。RFC 8201
收到 Too Big,改变的是 MSS 通告吗?
保留握手通告,才能看清后续调整究竟发生在哪个变量上。先丢掉大小反馈并重发,再重置、递送反馈、重发,可以对照两个不同的因果过程。历史结果不会因收到反馈而变成“那一包当时其实成功了”。
正在呈现知识画面 重新加载
收到 Too Big,改变的是 MSS 通告吗? 路径反馈更新当前发送预算,不会改写握手里的 MSS。没有反馈时,重复同样的大包仍可能失败;TCP 选项还要从包长中扣除。
{"id":"pmtu-feedback-and-segmentation","title":"收到 Too Big,改变的是 MSS 通告吗?","summary":"路径反馈更新当前发送预算,不会改写握手里的 MSS。没有反馈时,重复同样的大包仍可能失败;TCP 选项还要从包长中扣除。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"options\" data-i18n=\"options\"></label><output for=\"options\" id=\"options-value\"></output><input id=\"options\" type=\"range\" min=\"0\" max=\"40\" value=\"0\" step=\"4\"></div><div class=\"actions\"><button id=\"send\" data-i18n=\"send\"></button><button id=\"deliver\" data-i18n=\"deliver\"></button><button id=\"drop\" data-i18n=\"drop\"></button><button id=\"reset\" data-i18n=\"reset\"></button></div><div class=\"metrics\"><div><span data-i18n=\"mss\"></span><strong id=\"mss\"></strong></div><div><span data-i18n=\"estimate\"></span><strong id=\"estimate\"></strong></div><div><span data-i18n=\"data\"></span><strong id=\"data\"></strong></div></div><div id=\"packet\" class=\"budget\"></div><section class=\"panel\"><h3 data-i18n=\"last\"></h3><div id=\"last\"></div></section><p id=\"equation\" class=\"formula\"></p><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#last{min-height:5.4em;font:13px/1.8 var(--viz-font-ui);white-space:pre-line}.metrics strong{font-size:18px}","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 estimate=1500,pending=false,last='∅';function draw(){const options=+$('#options').value,data=Math.max(0,Math.min(1460-options,estimate-40-options));set('options-value',options);set('mss',1460);set('estimate',estimate);set('data',data);band('packet',[{name:'IPv4',value:20},{name:'TCP',value:20+options},{name:'data',value:data}],1500);set('last',last);set('equation','data ≤ min(1460 − '+options+', '+estimate+' − 20 − 20 − '+options+') = '+data);$('#deliver').disabled=$('#drop').disabled=!pending;$('#data').dataset.value=data;}$('#send').onclick=()=>{const bytes=40+ +$('#options').value+ +$('#data').textContent;pending=bytes>1492;last=bytes+' B → '+(pending?'drop; Too Big(1492) pending':'delivered');draw()};$('#deliver').onclick=()=>{estimate=Math.min(estimate,1492);pending=false;draw()};$('#drop').onclick=()=>{pending=false;draw()};$('#reset').onclick=()=>{estimate=1500;pending=false;last='∅';draw()};$('#options').oninput=draw;draw();","audio":false,"strings":{"data":"下一包数据上限","deliver":"递送匹配的 Too Big","drop":"丢弃此次大小反馈","estimate":"当前 PMTU 估计","heading":"收到 Too Big,改变的是 MSS 通告吗?","intro":"先发送,再决定大小反馈能否到达。观察路径估计、分段预算与已通告 MSS 分别保留。","last":"上次发送结果","mss":"握手通告 MSS","options":"TCP 选项字节","reset":"重置","scope":"IPv4无IP选项、DF=1,握手MSS1460,初始路径估计1500。实际瓶颈1492;这里只模拟匹配反馈和下一次分段,不实现完整TCP恢复或PLPMTUD。","scopeLabel":"模型范围与约定","send":"按当前预算发送"}}
黑洞:按因果链收集证据
一次可信诊断可以依次确认:发送端确实发出某个大小的包;相应出口无法容纳;错误反馈在何处生成、到达或丢失;后续发送是否仍使用过大的包。若只能看端点,则结合可控包长变化、重传和应用进度缩小范围,同时保留其他原因。
观察点 支持的判断 下一步
大包本机发送即失败 本地接口/路径估计已限制发送 核对错误、接口 MTU 与目的路由
收到匹配的 Too Big 路径给出了大小约束 看发送端是否接受及缩小
小包稳定通过、大包反复无确认 大小相关故障候选 排除协议过滤、随机丢包与探测路径差异
缩小后恢复 修改与恢复有相关性 结合原反馈/出口证据判断根因
抓包还需注意 TSO/GSO/GRO:主机上的“巨包”可能是分段之前或聚合之后的视图,不一定曾以该大小在线上传输。MSS clamping 只涉及 TCP,不能修复所有 UDP/QUIC 路径问题。
PLPMTUD 与 Linux 的实现开关
分包层可以利用探测和明确确认来验证某个包长是否可用,减少对 ICMP 唯一反馈通路的依赖。DPLPMTUD 需要基础大小、向上搜索、探测失败重试与黑洞恢复等状态;单次大包丢失可能只是拥塞,不能简单二分一次就宣布最大值。若某 UDP 应用没有确认机制,也不能凭本地 send 成功断言远端收到。RFC 8899:DPLPMTUD
Linux net.ipv4.tcp_mtu_probing 文档区分 0(禁用)、1(检测到相应黑洞时启用)和 2(始终启用)。它是 TCP 实现行为,不是全局修复所有传输协议的开关;是否适用要结合内核、连接和网络策略。本篇保留机制说明,不用一个无条件 sysctl 写入代替诊断。Linux IP sysctl 文档
本地校验与长度预算
import struct
def checksum ( data ) :
if len ( data ) % 2 :
data += bytes ( 1 )
total = sum ( struct . unpack ( ' !%d H' % ( len ( data ) / / 2 ) , data ) )
while total > > 16 :
total = ( total & 65535 ) + ( total > > 16 )
return ( ~ total ) & 65535
message = bytearray ( struct . pack ( ' !BBHHH' , 8 , 0 , 0 , 0x 1234, 7 ) + b ' hello' )
message [ 2 : 4 ] = struct . pack ( ' !H' , checksum ( message ) )
assert checksum ( message ) == 0
message [ - 1 ] ^= 1
assert checksum ( message ) != 0
assert 1500 - 20 - 8 == 1472
assert 1500 - 40 - 8 == 1452
assert 1492 - 20 - 20 == 1452
assert 1492 - 20 - 32 == 1440
assert 1464 + 8 + 20 == 1492
print ( ' ICMP 一补校验、奇数字节填充及 MTU 数据预算通过' )
这里仅构造 ICMPv4 Echo 的局部消息并计算长度;没有发送网络探测,也没有改变 PMTU、MSS 或 sysctl。
继续查阅