-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathtraceroute.html
More file actions
250 lines (232 loc) · 18.3 KB
/
Copy pathtraceroute.html
File metadata and controls
250 lines (232 loc) · 18.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>traceroute 使用指南</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: #FAF9F6; min-height: 100vh; padding: 2.5rem 2rem; color: #1a1a1a;
}
.container { max-width: 1000px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.375rem; }
.pulse-dot { width: 10px; height: 10px; background: #C77DFF; border-radius: 50%; animation: pulse 2.5s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity:1; transform:scale(1);} 50% { opacity:0.6; transform:scale(0.9);} }
h1 { font-size: 1.375rem; font-weight: 700; letter-spacing: -0.02em; }
.subtitle { color: #8B8680; font-size: 0.8125rem; margin-left: 1.625rem; }
.diagram-desc { color: #B0AAA4; font-size: 0.6875rem; margin-top: 0.5rem; margin-left: 1.625rem; line-height: 1.5; font-style: italic; }
.back-link { display: inline-block; margin-top: 0.75rem; margin-left: 1.625rem; font-size: 0.75rem; color: #2D8E9E; text-decoration: none; transition: color 0.2s; }
.back-link:hover { color: #D97757; }
h2 { font-size: 1.125rem; font-weight: 700; margin-top: 2.5rem; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid #E8E4DE; }
h3 { font-size: 0.9375rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.75rem; }
p { color: #4A4540; font-size: 0.8125rem; line-height: 1.7; margin-bottom: 0.75rem; }
ul.body { color: #4A4540; font-size: 0.8125rem; line-height: 1.7; margin: 0 0 0.75rem 1.5rem; }
.section { background: #FFFFFF; border-radius: 12px; border: 1px solid #E8E4DE; padding: 1.5rem 1.75rem; margin-bottom: 1.25rem; box-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.02); }
.section-title { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; }
.section-dot { width: 8px; height: 8px; border-radius: 50%; }
.section-dot.teal { background: #2D8E9E; }
.section-dot.plum { background: #8B6F8A; }
.section-dot.sage { background: #5B8A72; }
.section-dot.slate { background: #6B7280; }
.section-dot.terracotta { background: #D97757; }
pre { background: #1E1E1E; color: #E8E8E8; border-radius: 8px; padding: 1rem 1.25rem; font-family: 'SF Mono','Fira Code',monospace; font-size: 0.72rem; line-height: 1.6; overflow-x: auto; margin: 0.75rem 0; }
code { font-family: 'SF Mono','Fira Code',monospace; font-size: 0.78em; background: #F0EDE8; padding: 0.1rem 0.35rem; border-radius: 4px; color: #B5482E; }
pre code { background: none; padding: 0; color: inherit; }
table { width: 100%; border-collapse: collapse; font-size: 0.75rem; margin: 0.75rem 0; }
th, td { text-align: left; padding: 0.5rem 0.6rem; border-bottom: 1px solid #EEE9E3; }
th { color: #8B8680; font-weight: 600; }
td code { font-size: 0.72rem; }
.badge { display: inline-block; font-size: 0.6rem; font-weight: 600; padding: 0.15rem 0.45rem; border-radius: 5px; margin-right: 0.35rem; vertical-align: middle; }
.badge-probe { background: #EFE0F5; color: #8B5FA8; }
.badge-icmp { background: #E0F0F5; color: #2D7E8E; }
.badge-conc { background: #E3F1E8; color: #45785A; }
.note { background: #FFF5F0; border-left: 3px solid #D97757; border-radius: 0 8px 8px 0; padding: 0.75rem 1rem; font-size: 0.75rem; color: #6B6560; margin: 0.75rem 0; }
.note strong { color: #D97757; }
.tip { background: #F0F8F8; border-left: 3px solid #2D8E9E; border-radius: 0 8px 8px 0; padding: 0.75rem 1rem; font-size: 0.75rem; color: #6B6560; margin: 0.75rem 0; }
.tip strong { color: #2D8E9E; }
.toc { background: #FDFCFB; border: 1px solid #E8E4DE; border-radius: 10px; padding: 1.25rem 1.5rem; margin-bottom: 2rem; }
.toc h3 { font-size: 0.8125rem; margin-top: 0; margin-bottom: 0.75rem; color: #8B8680; }
.toc ul { list-style: none; font-size: 0.75rem; }
.toc li { margin-bottom: 0.375rem; }
.toc a { color: #2D8E9E; text-decoration: none; transition: color 0.2s; }
.toc a:hover { color: #D97757; }
.footer { text-align: center; margin-top: 2rem; color: #B0AAA4; font-size: 0.6875rem; letter-spacing: 0.02em; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="header-row">
<div class="pulse-dot"></div>
<h1>traceroute 使用指南</h1>
</div>
<p class="subtitle">nettools · 逐跳路径探测工具(ICMP/UDP/TCP)</p>
<p class="diagram-desc"><b>Usage Guide</b> — 并发探测多个 TTL 与多个目标,输出每跳 RTT 与丢包率;报文编解码全部基于 goscapy。</p>
<a class="back-link" href="index.html">← 返回首页</a>
</div>
<div class="toc">
<h3>目录</h3>
<ul>
<li><a href="#overview">1. 概述</a></li>
<li><a href="#how">2. 工作原理</a></li>
<li><a href="#proto">3. 三种探测协议</a></li>
<li><a href="#concurrency">4. 并发模型</a></li>
<li><a href="#goscapy">5. goscapy 编解码</a></li>
<li><a href="#quickstart">6. 快速开始</a></li>
<li><a href="#cli">7. 命令行参数</a></li>
<li><a href="#output">8. 输出格式</a></li>
<li><a href="#enrich">9. IP 信息增强(ASN / 归属地)</a></li>
<li><a href="#scenarios">10. 使用场景</a></li>
<li><a href="#faq">11. FAQ</a></li>
</ul>
</div>
<div class="section" id="overview">
<div class="section-title"><div class="section-dot plum"></div><h3>1. 概述</h3></div>
<p>
<span class="badge badge-probe">Probe</span>
<span class="badge badge-icmp">ICMP/UDP/TCP</span>
<span class="badge badge-conc">Concurrent</span>
traceroute 是面向 IPv4 的逐跳路径探测工具,用于定位丢包/高时延发生在哪一跳、确认 ECMP 多路径下的实际转发路径。它支持 <strong>ICMP Echo</strong>、<strong>UDP</strong>、<strong>TCP SYN</strong> 三种探针,并发探测多个 TTL 与多个目标,无需服务端部署。
</p>
<p>所有探测报文的<strong>构造与解析全部通过 <a href="https://github.com/smallnest/goscapy" target="_blank">smallnest/goscapy</a></strong> 完成,不存在手写的字节序或校验和逻辑。运行需要 raw socket,即 root / <code>CAP_NET_RAW</code> 权限。</p>
</div>
<div class="section" id="how">
<div class="section-title"><div class="section-dot teal"></div><h3>2. 工作原理</h3></div>
<p>traceroute 利用 IP 报文的 <strong>TTL(生存时间)</strong>字段:每经过一个路由器,TTL 减 1;当 TTL 减到 0 时,该路由器会丢弃报文并回送一个 <strong>ICMP Time Exceeded</strong>(类型 11)报文,其源 IP 就是这一跳的地址。</p>
<ul class="body">
<li>从 TTL=1 开始,对每个 TTL 发送 <code>--queries</code> 个探针;</li>
<li>中间路由器回送 ICMP Time Exceeded,工具解析其中<strong>内嵌的原始 IP+L4 头部</strong>,把回包匹配回对应探针;</li>
<li>到达目标的判定按协议而定(见下一节),命中后停止递增 TTL。</li>
</ul>
<div class="note"><strong>注意:</strong>ICMP 错误报文会引用原始探测包的 IP 头 + 前 8 字节负载。traceroute 用 goscapy 解析这段被引用的内层报文(内层目的 IP、ICMP id/seq、UDP/TCP 端口)来确认归属,并发时不会串包。</div>
</div>
<div class="section" id="proto">
<div class="section-title"><div class="section-dot sage"></div><h3>3. 三种探测协议</h3></div>
<table>
<thead><tr><th>协议</th><th>探针</th><th>到达目标的判定</th><th>适用场景</th></tr></thead>
<tbody>
<tr><td><code>icmp</code>(默认)</td><td>ICMP Echo Request,id=pid、seq 编码 ttl/probe</td><td>收到目标的 ICMP Echo Reply</td><td>最通用</td></tr>
<tr><td><code>udp</code></td><td>UDP,目的端口随 TTL 递增(默认 33434 起)</td><td>收到目标的 ICMP Port Unreachable</td><td>传统 traceroute 行为,穿透性好</td></tr>
<tr><td><code>tcp</code></td><td>TCP SYN,固定目的端口(默认 80/443)</td><td>收到目标的 SYN-ACK 或 RST</td><td>穿越只放行特定 TCP 端口的防火墙</td></tr>
</tbody>
</table>
<p>三者共用同一套「TTL 递增 + ICMP Time Exceeded 收集 + 多探针采样 + 逐跳统计」框架,仅探针发送与目标到达判定不同,用 <code>--protocol</code> 切换。</p>
<div class="tip"><strong>UDP/TCP 源目端口与源目 IP:</strong>UDP/TCP 探测下可用 <code>--src-port</code> 固定源端口、<code>--fixed-dport</code> 固定 UDP 目的端口(默认按 TTL 递增)、<code>--src-ip</code> 伪造源 IP、<code>--dst-ip</code> 覆盖写入探针的目的 IP(独立于显示的目标),便于验证特定五元组/转发策略。这些选项对 ICMP 无效。源端口为 0 时按 (ttl,probe) 自动分配,保证并发探针可区分;固定源端口会牺牲源端口维度的区分(仍按内嵌头其余字段匹配)。</div>
</div>
<div class="section" id="concurrency">
<div class="section-title"><div class="section-dot terracotta"></div><h3>4. 并发模型</h3></div>
<ul class="body">
<li><strong>跳间并发:</strong>不必等上一跳结束,同时对 TTL=1..max-hops 的所有探针发起探测;</li>
<li><strong>多目标并发:</strong>一次命令对多个目标主机并行 traceroute;</li>
<li><strong>可控并发度:</strong><code>--parallel</code>(默认 16)限制在途探针总数,避免触发中间设备 ICMP 限速。</li>
</ul>
<p>每个探针通过 goscapy 的 <code>Sr1</code> 发送并等待首个匹配回包;匹配函数依据内嵌原始报文的标识字段(ICMP id/seq、UDP/TCP 端口)路由回包,确保并发下回包归属正确。</p>
</div>
<div class="section" id="goscapy">
<div class="section-title"><div class="section-dot plum"></div><h3>5. goscapy 编解码</h3></div>
<p>探针用 goscapy builder 构造,IP/ICMP/UDP/TCP 校验和自动计算:</p>
<pre><code>// ICMP 探针,TTL 递增
ip := layers.NewIP()
ip.Set("src", srcIP); ip.Set("dst", dstIP); ip.Set("ttl", uint8(ttl))
icmp := layers.NewICMPEcho(pid, uint16(ttl)<<8|uint16(probe))
pkt := ip.Over(icmp)
// 回包解析(含 ICMP 错误内嵌的原始报文头)
resp, _ := packet.DissectByProto(raw, "IP")
icmpType, _ := resp.GetLayer("ICMP").Get("type")</code></pre>
</div>
<div class="section" id="quickstart">
<div class="section-title"><div class="section-dot teal"></div><h3>6. 快速开始</h3></div>
<p>编译:</p>
<pre><code>go build -o traceroute ./cmd/traceroute/
# 或
make build-traceroute</code></pre>
<p>运行(raw socket 需要 root / CAP_NET_RAW):</p>
<pre><code># ICMP traceroute(默认)
sudo ./traceroute example.com
# UDP traceroute,指定起始端口
sudo ./traceroute -p udp --port 33434 8.8.8.8
# TCP SYN traceroute 到 443 端口(适合穿越防火墙)
sudo ./traceroute -p tcp --port 443 example.com
# 一次并发探测多个目标
sudo ./traceroute 8.8.8.8 1.1.1.1 example.com</code></pre>
</div>
<div class="section" id="cli">
<div class="section-title"><div class="section-dot slate"></div><h3>7. 命令行参数</h3></div>
<table>
<thead><tr><th>短</th><th>长</th><th>默认</th><th>说明</th></tr></thead>
<tbody>
<tr><td><code>-p</code></td><td><code>--protocol</code></td><td>icmp</td><td>探测协议:icmp / udp / tcp</td></tr>
<tr><td><code>-m</code></td><td><code>--max-hops</code></td><td>30</td><td>最大跳数(TTL)</td></tr>
<tr><td><code>-q</code></td><td><code>--queries</code></td><td>3</td><td>每跳探针数量</td></tr>
<tr><td></td><td><code>--port</code></td><td>33434(udp)/80(tcp)</td><td>UDP/TCP 目标端口</td></tr>
<tr><td></td><td><code>--src-port</code></td><td>0</td><td>UDP/TCP 源端口(0 = 按探针自动分配)</td></tr>
<tr><td></td><td><code>--fixed-dport</code></td><td>false</td><td>UDP 目标端口固定为 --port(不随 TTL 递增)</td></tr>
<tr><td></td><td><code>--src-ip</code></td><td>auto</td><td>覆盖 UDP/TCP 探针源 IPv4(伪造源 IP)</td></tr>
<tr><td></td><td><code>--dst-ip</code></td><td>target</td><td>覆盖写入 UDP/TCP 探针的目的 IPv4</td></tr>
<tr><td><code>-w</code></td><td><code>--timeout</code></td><td>1s</td><td>单探针超时</td></tr>
<tr><td></td><td><code>--no-dns</code></td><td>false</td><td>关闭反向 DNS 解析</td></tr>
<tr><td></td><td><code>--asn</code></td><td>false</td><td>经 Team Cymru DNS 为每跳标注起源 ASN / AS 名 / BGP 前缀(仅出站 DNS,无需 API key)</td></tr>
<tr><td></td><td><code>--geo</code></td><td>false</td><td>经 ipinfo.io 为每跳标注归属地/运营商(会把每跳 IP 发往第三方)</td></tr>
<tr><td></td><td><code>--geo-token</code></td><td>空</td><td>ipinfo.io API token(可选;为空则用匿名额度)</td></tr>
<tr><td><code>-t</code></td><td><code>--tos</code></td><td>0</td><td>IP TOS/DSCP 值</td></tr>
<tr><td></td><td><code>--parallel</code></td><td>16</td><td>并发在途探针上限</td></tr>
<tr><td><code>-I</code></td><td><code>--interface</code></td><td>auto</td><td>出向网卡(自动探测)</td></tr>
<tr><td><code>-l</code></td><td><code>--local-addr</code></td><td>auto</td><td>本地源 IPv4(自动探测)</td></tr>
<tr><td><code>-V</code></td><td><code>--version</code></td><td>false</td><td>打印版本并退出</td></tr>
</tbody>
</table>
</div>
<div class="section" id="output">
<div class="section-title"><div class="section-dot sage"></div><h3>8. 输出格式</h3></div>
<p>输出风格对齐传统 <code>traceroute</code> / <a href="https://github.com/aeden/traceroute" target="_blank">aeden/traceroute</a>,每跳一行,附带各探针 RTT;无响应的探针显示为 <code>*</code>,ECMP 同 TTL 多个回源就地展示。</p>
<pre><code>traceroute to example.com (93.184.216.34), 30 hops max, ICMP probes, 60 byte packets
1 gateway (192.168.1.1) 1.234ms 1.301ms 1.122ms
2 10.0.0.1 (10.0.0.1) 5.678ms 5.521ms *
3 * * *
4 core1.isp.net (203.0.113.5) 12.345ms 12.201ms 12.503ms</code></pre>
</div>
<div class="section" id="enrich">
<div class="section-title"><div class="section-dot plum"></div><h3>9. IP 信息增强(ASN / 归属地)</h3></div>
<p>可选地为每跳 IP 附加 <strong>ASN / AS 名 / BGP 前缀</strong>与<strong>地理归属地</strong>信息。数据来源做成<strong>可插拔的 Provider 接口</strong>(<code>traceroute/enrich</code> 包),默认两种实现,未来可替换为离线 MaxMind、bgp.tools、内部服务等。</p>
<table>
<thead><tr><th>开关</th><th>数据源</th><th>提供字段</th><th>依赖</th></tr></thead>
<tbody>
<tr><td><code>--asn</code></td><td>Team Cymru IP-to-ASN DNS</td><td>起源 ASN、AS 名、覆盖 BGP 前缀</td><td>仅出站 DNS,<strong>无需 API key</strong></td></tr>
<tr><td><code>--geo</code></td><td>ipinfo.io HTTP API</td><td>国家、省/州、城市、运营商</td><td>需访问外网,可选 <code>--geo-token</code></td></tr>
</tbody>
</table>
<p>两者均<strong>默认关闭</strong>,不开启时行为与原版完全一致。查询在所有探测完成后统一并发执行,并按 IP 去重(同一 ASN 的 AS 名也会缓存),不影响探测路径本身。</p>
<div class="note"><strong>隐私提醒:</strong><code>--geo</code> 会把每个命中跳的 IP 发送给第三方(ipinfo.io)。在敏感网络环境中请优先只用 <code>--asn</code>(纯 DNS),或接入离线数据源。</div>
<pre><code># 标注 ASN / BGP 前缀(纯 DNS)
sudo ./traceroute --asn 8.8.8.8
# 同时标注归属地(带 token 提升额度)
sudo ./traceroute --asn --geo --geo-token <your-token> example.com</code></pre>
<p>输出会在每跳行尾追加方括号注释,格式 <code>[ASN AS名 前缀 | 国家 省 城市]</code>:</p>
<pre><code>traceroute to dns.google (8.8.8.8), 30 hops max, ICMP probes, 60 byte packets
1 gateway (192.168.1.1) 1.234ms 1.301ms 1.122ms
2 core1.isp.net (203.0.113.5) [AS4134 CHINANET-BACKBONE 203.0.113.0/24 | CN Beijing] 12.3ms 12.2ms
3 dns.google (8.8.8.8) [AS15169 GOOGLE 8.8.8.0/24 | US Mountain View] 30.1ms 29.8ms</code></pre>
</div>
<div class="section" id="scenarios">
<div class="section-title"><div class="section-dot terracotta"></div><h3>10. 使用场景</h3></div>
<ul class="body">
<li><strong>逐跳故障定位:</strong>在大规模物理网络与 AI 训练集群中定位丢包/高时延发生在哪一跳。</li>
<li><strong>ECMP 路径确认:</strong>识别同一 TTL 出现的多个回源,确认多路径转发。</li>
<li><strong>防火墙穿越探测:</strong>用 TCP SYN(如 <code>--port 443</code>)或 UDP 探针穿越对 ICMP 限速/过滤的网络。</li>
<li><strong>多目标并发路径采集:</strong>一次命令并发 trace 多个目标。</li>
</ul>
</div>
<div class="section" id="faq">
<div class="section-title"><div class="section-dot slate"></div><h3>11. FAQ</h3></div>
<p><strong>为什么需要 root?</strong> 构造自定义 IP/ICMP/UDP/TCP 报文并设置 TTL 需要 raw socket,需 root 或 <code>CAP_NET_RAW</code>(<code>sudo setcap cap_net_raw+ep ./traceroute</code>)。</p>
<p><strong>支持 IPv6 吗?</strong> 当前实现优先 IPv4;包结构预留了 IPv6(HopLimit)扩展,与 ping/ping6 的双栈风格保持一致。</p>
<p><strong>某些跳一直是 <code>*</code>?</strong> 该跳路由器可能对 ICMP Time Exceeded 限速或不回送,属正常现象,不一定代表丢包。</p>
</div>
<div class="footer">nettools · traceroute — Produced by Baidu System Department</div>
</div>
</body>
</html>