能 ping 通同网段,却访问不了远端,先看路由选出的下一跳,再看该下一跳的邻居解析。ARP/NDP 解决的是当前链路上的邻接问题,不负责寻找互联网中的目标位置。缓存已有有效映射、点到点链路或其他邻接机制也可能不需要逐次发送解析请求。
| 问题 | 本篇入口 | 不要混淆 |
| ARP 应该问网关还是服务器 | 路由与解析顺序 | 最终 IP 与下一跳 IP |
| 28 B ARP 为什么抓到 42/60 B | 报文字节账 | 协议、以太网头、填充和 FCS |
| STALE 是否必须清空 | 邻居状态 | 可用映射与近期可达证明 |
| IPv6 如何定向发现邻居 | NS/NA 与组播 | 地址解析、可达性探测与 DAD |
| RA 怎样影响地址和默认路由 | 自动配置 | 前缀 L/A 标志、地址生命周期、路由器生命周期 |
| 加密隧道是否解决欺骗 | 安全边界 | 身份认证、转发与可用性 |
从一次跨网段发送讲起
假设 A 为 192.0.2.10/24,默认网关为 192.0.2.1,要访问 198.51.100.20。路由查询选择网关为下一跳,A 查询所在接口的邻居表;没有映射时,才解析 192.0.2.1 的 MAC,而不是对远端 198.51.100.20 广播。
接口也是邻居上下文的一部分:相同 IPv4 地址可能出现在不同 VLAN 或网络命名空间中,不能用一个全局“IP→MAC 字典”描述所有系统状态。路由错误、VLAN 不通或接口选择错误,都可能表现为邻居解析失败。
ARP 报文与最小帧长度
Ethernet 上的 IPv4 ARP 使用 EtherType 0x0806,直接承载在以太网中,不经过 IPv4 头。它的通用字段允许不同地址长度,常见 Ethernet/IPv4 组合如下。RFC 826
| 字段 | 字节数 | 本例值 |
| Hardware Type / Protocol Type | 2 + 2 | 1 / 0x0800 |
| Hardware Length / Protocol Length | 1 + 1 | 6 / 4 |
| Operation | 2 | 1 请求,2 应答 |
| Sender Hardware / Protocol Address | 6 + 4 | 发送者 MAC / IPv4 |
| Target Hardware / Protocol Address | 6 + 4 | 目标 MAC / IPv4 |
合计 28 B。加未标记以太网头 14 B 后只有 42 B,还需 18 B 填充才到不含 FCS 的 60 B,最后加 4 B FCS 为 64 B。这里的 46 B 最小以太网负载由 28 B ARP 和 18 B 填充组成,不包含以太网头与 FCS。抓包时填充和 FCS 是否可见又取决于位置与驱动。
初次解析请求通常以广播 MAC 发送,目标硬件地址尚未知;常见实现填零,但它不是用来决定广播范围的以太网目的地址。普通回复通常单播,也存在公告等不同使用形式,不能把所有 ARP 请求/应答方向固定化。
邻居表:有 MAC 与已确认可达是两件事
Linux 将 IPv4 ARP 与 IPv6 邻居行为纳入通用邻居框架。INCOMPLETE 表示正在解析,待发分组可在有限队列中等待;REACHABLE 表示近期得到相应可达确认;STALE 保留映射但确认已旧,不是条目“坏了”。
使用 STALE 条目时,通常可以先用已有 MAC 发出分组,再进入 DELAY 等待来自上层等途径的确认;必要时转 PROBE 主动探测。若所有探测耗尽则可进入 FAILED,但表项回收由后续生命周期/垃圾回收处理,不保证立即从 ip neigh 消失。收到的报文是否构成可达证明也取决于类型与状态,并非任何 ARP/NA 都直接置 REACHABLE。
本篇按Linux 7.2 的 net/core/neighbour.c 核对了 STALE→DELAY、等待确认和探测失败路径。实际还有永久、外部管理条目等特殊状态,图只展示常见路径;REACHABLE 到期也会结合近期使用直接进入 DELAY 等状态,不能把图当作逐行实现。
base_reachable_time_ms 是可达时间基准,实际可达时间有随机化等处理;delay_first_probe_time、retrans_time_ms、ucast_solicit、mcast_solicit 分别影响等待和探测。gc_stale_time 与未使用条目的回收资格相关,不是每条 STALE 的固定存活倒计时。配置还要区分默认模板与具体接口,不宜未看实际状态就按固定 30/60 秒“调优”。
有 MAC,为什么还要确认可达?
把映射是否存在、数据是否已发出和可达证明是否新鲜分成三项。试试直接从 STALE 发送,再选择确认;也可让等待耗尽,观察失败并不凭空把已经发出的数据撤回。清除映射后再发送,会走另一条需要排队的路径。
正在呈现知识画面
有 MAC,为什么还要确认可达?STALE 仍保留可用映射,首次使用可先发再等待确认。解析未知地址才需要暂存分组;探测失败与立即删除整项不是同一个事件。
{"id":"neighbor-reachability-evidence","title":"有 MAC,为什么还要确认可达?","summary":"STALE 仍保留可用映射,首次使用可先发再等待确认。解析未知地址才需要暂存分组;探测失败与立即删除整项不是同一个事件。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"send\" data-i18n=\"send\"></button><button id=\"wait\" data-i18n=\"wait\"></button><button id=\"confirm\" data-i18n=\"confirm\"></button><button id=\"forget\" data-i18n=\"forget\"></button><button id=\"stale\" data-i18n=\"stale\"></button></div><div class=\"metrics\"><div><span data-i18n=\"state\"></span><strong id=\"state\"></strong></div><div><span data-i18n=\"mapping\"></span><strong id=\"mapping\"></strong></div><div><span data-i18n=\"sent\"></span><strong id=\"sent\"></strong></div><div><span data-i18n=\"queued\"></span><strong id=\"queued\"></strong></div></div><div id=\"queue\" class=\"track\"></div><section class=\"panel\"><h3 data-i18n=\"trace\"></h3><div id=\"trace\"></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#trace{min-height:5.4em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui)}.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 state='STALE',mac=true,sent=0,queue=0,probes=0,log='STALE + MAC';function draw(){set('state',state);set('mapping',mac?'02:00:00:00:00:01':'∅');set('sent',sent);set('queued',queue);set('trace',log);cells('queue',Array.from({length:3},(_,i)=>({text:i<queue?'data':'·',cls:i<queue?'gold':'empty'})));$('#confirm').disabled=state==='FAILED';$('#wait').disabled=state==='STALE'||state==='FAILED';$('#send').disabled=queue===3;$('#state').dataset.snapshot=JSON.stringify({state,mac,sent,queue,probes});}$('#send').onclick=()=>{if(state==='FAILED'){state='INCOMPLETE';mac=false;probes=0}if(!mac){queue++;state='INCOMPLETE';log='data → queue'}else{sent++;if(state==='STALE')state='DELAY';log='data → '+state+'; MAC available'}draw()};$('#wait').onclick=()=>{if(state==='REACHABLE')state='STALE';else if(state==='DELAY'){state='PROBE';probes=1}else if(++probes>=2){state='FAILED';queue=0}log='wait → '+state+'; probes='+probes;draw()};$('#confirm').onclick=()=>{mac=true;state='REACHABLE';sent+=queue;queue=0;probes=0;log='valid evidence → REACHABLE';draw()};function reset(known){state=known?'STALE':'INCOMPLETE';mac=known;sent=queue=probes=0;log=state;draw()}$('#forget').onclick=()=>reset(false);$('#stale').onclick=()=>reset(true);draw();","audio":false,"strings":{"confirm":"收到有效确认","forget":"清除映射并重置","heading":"有 MAC,为什么还要确认可达?","intro":"自由选择发送、等待和确认,观察映射、待发队列与可达状态分别变化。","mapping":"链路映射","queued":"待发份数","reset":"重置","scope":"简化普通动态邻居;初态 STALE,等待分阶段而非真实秒数。两次探测和队列上限3是教材约定;确认表示匹配的有效可达证据。","scopeLabel":"模型范围与约定","send":"发送一份数据","sent":"已发出数据份数","stale":"从 STALE 重置","state":"邻居状态","trace":"最后一次操作","wait":"推进等待"}}
IPv4 冲突探测与地址公告不同
RFC 5227 的 ARP Probe 在尚未使用候选地址时探测冲突,发送者 IPv4 为 0.0.0.0,目标 IPv4 为候选地址;这样避免先把尚未确认的映射写进其他主机缓存。ARP Announcement 则声明正在使用的地址,发送者与目标 IPv4 相同。把两者都写成“发一个 gratuitous ARP,等回复”会漏掉同时探测与冲突判断。RFC 5227:IPv4 地址冲突检测
故障切换时公告有助于更新邻居映射,交换设备又可从源 MAC 学习入口;这些是不同表。策略过滤、静态表项、MAC 不变或转发表尚未收敛,都可能影响切换结果,不能承诺发出公告后立即零丢包。
Proxy ARP 是某设备代目标应答并提供自己的 MAC,随后代为转发。前提是发送主机按路由/链路判断确实会解析该目标;若它已经判定目标经默认网关,就通常问网关。代理可用于明确设计的接入场景,也可能掩盖错误的 on-link 判断,应结合路由与策略解释其作用。
NDP 消息:正确编号与校验规则
NDP 由 ICMPv6 承载,基本消息包括 RS 133、RA 134、NS 135、NA 136、Redirect 137,这些消息的 Code 都为 0。共同头只有 Type、Code、Checksum 等通用部分,后续固定字段随消息类型变化,不能统一画成四字节 Reserved。校验和包含 IPv6 伪首部;接收验证还检查 Hop Limit=255 等条件,以限制被跨路由转发的伪造报文,但不认证本地发送者。RFC 4861:消息格式与验证
| 常见 ND Option 类型 | 含义 |
| 1 | Source Link-Layer Address |
| 2 | Target Link-Layer Address |
| 3 | Prefix Information |
| 4 | Redirected Header |
| 5 | MTU |
这些选项的 Length 以 8 B 为单位且包含类型和长度字段;零长度无效。以太网链路地址选项常为 8 B,不能把 Type 1 同时称为源/目标地址,再把后续编号整体错移。
NS/NA:组播缩小范围,但一个组可对应多个地址
初始解析通常向目标的 solicited-node 组播地址发 NS。该地址为 ff02::1:ff00:0/104 加目标 IPv6 最低 24 bit;以太网 IPv6 组播 MAC 使用 33:33 加 IPv6 组播地址最低 32 bit。因此目标 2001:db8::1234:5678 对应 ff02::1:ff34:5678 和 33:33:ff:34:56:78。RFC 2464:IPv6 组播到以太网映射
多个 IPv6 地址可能共享低 24 bit,也就共享该组;接收者仍需核对 NS 的完整 Target Address,不能仅凭以太网目的 MAC 判断“就是找我”。已知邻居的可达性探测又可能使用单播 NS,NS 不只是一种组播问询。
普通被请求的 NA 可单播返回,携带目标链路地址及 R/S/O 标志。R 指示路由器角色,S 表示对应的被请求可达确认语义,O 影响已有映射更新;组播 NA 的 Solicited 标志不能按“它也是回应”一概置 1。缓存更新要结合原状态和标志执行。RFC 4861:地址解析与 NA 处理
DAD:还没有地址时怎样问有没有冲突
普通 DAD 中,候选地址处于 tentative,发出的 NS 源为 ::,Target 为候选地址,不带 Source Link-Layer Address 选项。已有地址所有者可以回复相应 NA;两个同时探测相同地址的节点也可能通过收到对方的 NS 发现冲突,因此不能只统计 NA,或认为一定只有“第二台”失败。
没有观察到冲突才按协议流程启用地址,但丢包、过滤和链路分隔意味着 DAD 不是唯一性的绝对证明。启用次数、适用例外、优化 DAD 与操作系统策略都要区分,不能对任何类型地址一概规定相同流程。RFC 4862:DAD
RA:地址前缀、on-link 与默认路由分别生效
主机可通过 RS 请求路由器信息,路由器也可周期发送 RA;RA 来源通常为路由器链路本地地址。响应目的需要按 RS 来源等条件选择,源为 :: 的 RS 不能要求 RA 单播回一个未指定地址。
Prefix Information 的 L 表示前缀的 on-link 信息,A 表示能否用于自主地址配置,两者独立。拥有一个地址/前缀长度不应被无条件解释成所有匹配地址均直接可达;主机需遵循 on-link 信息与路由选择。RFC 5942:IPv6 子网模型
地址的 Preferred Lifetime 到期会进入 deprecated,通常避免为新通信选择它,但既有通信在仍有效时可继续;Valid Lifetime 到期后地址不再有效。RA Router Lifetime 则控制该路由器作为默认路由候选的期限,设为 0 不等于同时撤销所有前缀与地址。具体生命周期更新还有协议规则,不能给它们统一设为固定天数。
SLAAC 的 IID 可以使用适当的稳定不透明机制或临时地址策略,不应默认从 MAC 生成。RFC 8981 的临时地址周期性变化,降低特定地址关联风险,但它不隐藏全部网络标识,也不是所有 Linux 安装默认启用;系统配置与地址选择策略需要实际核对。RFC 8981:临时地址扩展
地址与默认路由,会一起过期吗?
公式里的不同寿命属于不同对象。选择 t=70 时,地址可以仍有效但已不再首选,而这份 RA 提供的默认路由已经过期;关掉 L 也不会自动关掉 A。时间滑杆只观察一次初始通告的后续结果,修改标志则重新构造这份通告。
正在呈现知识画面
地址与默认路由,会一起过期吗?A 与 L 独立;preferred 到期只使地址弃用,valid 到期才使其无效。Router Lifetime 另管默认路由候选。
{"id":"ra-independent-lifetimes","title":"地址与默认路由,会一起过期吗?","summary":"A 与 L 独立;preferred 到期只使地址弃用,valid 到期才使其无效。Router Lifetime 另管默认路由候选。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"check\"><input type=\"checkbox\" id=\"a\" checked><span data-i18n=\"a\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"l\" checked><span data-i18n=\"l\"></span></label><div class=\"control\"><label for=\"time\" data-i18n=\"time\"></label><output for=\"time\" id=\"time-value\"></output><input id=\"time\" type=\"range\" min=\"0\" max=\"120\" value=\"0\" step=\"1\"></div><div id=\"lifetimes\" class=\"intervals\"></div><div class=\"metrics\"><div><span data-i18n=\"address\"></span><strong id=\"address\"></strong></div><div><span data-i18n=\"onlink\"></span><strong id=\"onlink\"></strong></div><div><span data-i18n=\"router\"></span><strong id=\"router\"></strong></div></div><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.metrics strong{font-size:17px}.intervals{position:relative}.intervals:after{content:\"\";position:absolute;top:0;bottom:0;left:var(--now);width:2px;background:var(--ink);pointer-events:none}","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;}));}function draw(){const time=+$('#time').value,a=$('#a').checked,l=$('#l').checked,addr=!a?'∅':time<40?'preferred':time<100?'deprecated':'invalid';set('time-value',time);intervals('lifetimes',[{start:0,end:40,label:'preferred: 40 s'},{start:0,end:100,label:'valid: 100 s',cls:'gold'},{start:0,end:60,label:'router: 60 s'}],120);$('#lifetimes').style.setProperty('--now',100*time/120+'%');set('address',addr);set('onlink',t(l&&time<100?'yes':'no'));set('router',t(time<60?'yes':'no'));set('equation','t = '+time+'\\naddress valid: '+(a&&time<100)+'; default router: '+(time<60));$('#address').dataset.state=addr;$('#router').dataset.valid=String(time<60);$('#onlink').dataset.valid=String(l&&time<100);}document.querySelectorAll('input').forEach(e=>e.oninput=draw);draw();","audio":false,"strings":{"a":"PIO A:允许自主配置","address":"地址状态","heading":"地址与默认路由,会一起过期吗?","intro":"改变 PIO 标志与时间,比较独立的地址、前缀与路由状态。","l":"PIO L:发布 on-link 前缀","no":"不存在","onlink":"此 PIO 的 on-link 信息","reset":"重置","router":"默认路由候选","scope":"单次初始 RA,合适/64前缀,DAD成功,无后续通告或其他路由。寿命为教材秒数:preferred40、valid100、router60;不模拟更新时的两小时规则。","scopeLabel":"模型范围与约定","time":"距收到通告的时间(s)","yes":"存在"}}
安全:加密保护内容,不修复邻居可用性
ARP/NDP 的基本地址声明缺少普遍的源认证,本链路伪造可影响映射、路由或地址配置。接入控制、适当的 ARP 检查/绑定与 RA 防护要结合可信入口和地址来源设计;静态邻居条目可以固定部分映射,但带来维护负担,也不能保护全部链路行为。
经过正确认证的 TLS 或加密隧道可保护所覆盖会话的内容与完整性,但攻击者仍可能丢包、阻断握手或制造链路不可达。因此“用了 WireGuard,ARP 欺骗就无效”不成立;保密性、身份与可用性应分别判断。
本地报文与组播映射复算
import struct
from ipaddress import IPv4Address, IPv6Address
sha = bytes.fromhex('020000000001')
arp = struct.pack('!HHBBH', 1, 0x0800, 6, 4, 1)
arp += sha + IPv4Address('192.0.2.10').packed
arp += bytes(6) + IPv4Address('192.0.2.1').packed
assert len(arp) == 28
padding = max(0, 46-len(arp))
assert padding == 18 and 14+len(arp)+padding+4 == 64
assert struct.unpack('!H', arp[6:8])[0] == 1
target = IPv6Address('2001:db8::1234:5678')
solicited = IPv6Address(int(IPv6Address('ff02::1:ff00:0')) | (int(target)&0xffffff))
assert str(solicited) == 'ff02::1:ff34:5678'
mac = bytes.fromhex('3333') + solicited.packed[-4:]
assert mac.hex(':') == '33:33:ff:34:56:78'
assert bytes([1, 1]) + sha == bytes.fromhex('0101020000000001')
print('ARP 字段/填充、请求操作码和 IPv6 组播映射通过')
仅在本地构造字节并复算,没有向网络发送 ARP/NS、清理邻居缓存或修改 sysctl。
继续查阅