traceroute 程式可以讓我們看到 IP 資料報從一台主機傳到另一台主機所經過的路由,該程式最早由 Van Jacobson 實作。
當然我們不可能原汁原味的模仿一遍 traceroute 程式,在這裡,我們隻需要把最關鍵的功能實作出來就算完成任務。
1. 牛刀小試
1.1 仿真環境測試
這裡使用 GNS3 軟體來模拟實驗,該工程位于路徑
unp/protocol/gns3/demo01
下。拓撲圖如圖 1 所示。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SMzQTOxkjM3Y2YjNjYzgDMzYzX2EzNxETM2IzLchDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
圖1 仿真環境拓撲圖
- 在 PC1 上執行 traceroute,跟蹤 PC1 到 PC3 的路徑
圖2 PC1 到 PC3 的路徑
- 在 PC3 上執行 traceroute,跟蹤 PC3 到 PC1 的路徑
圖3 PC3 到 PC1 的路徑
1.2 真實網絡測試
- 程式路徑
如果你已經 clone 過這個代碼了,請使用
git pull
更新一下。本節程式所使用的程式路徑是
unp/program/icmp/traceroute
.
- 運作結果
圖4 檢視到 192.168.165.1 這台路由器的路徑
上面的 tracert 是我們自己寫的程式,下面的 traceroute 是系統自帶的程式。可以看到,分别經過了 4 跳就到達了目的地。圖 5 是該網絡拓撲圖。
圖5 網絡拓撲示意圖
2. 程式設計
2.1 traceroute 程式的執行過程
- 發送一份 TLL 字段為 1 的 IP 資料報給目的主機。處理這份資料報的第一個路由器将 TTL 值減 1,并丢棄該資料報,并發回一份逾時 ICMP 封包(type=11, code=0),這樣就得到了該路徑的第一個路由器位址。
- 發送一份 TLL 字段為 2 的 IP 資料報給目的主機,處理這份資料報的第一個路由器将 TLL 值減 1,并轉發給第二個路由器;第二個路由器将 TLL 值減 1,并丢棄資料報,并發回一份逾時 ICMP 封包。
- ……
在 traceroute 中,它實際發送一份 UDP 資料報給目的主機,但它選一個不可能的值作為 UDP 端口,目的主機任何一個應用程式都不可能使用這個端口。
最後,當該資料報到達目的主機時,會産生一個端口不可達的 ICMP 差錯封包(見上一篇文章),traceroute 程式要做的就是區分接收到的 icmp 封包是逾時還是端口不可達,以此判斷什麼時候應該結束。
2.2 僞代碼
完整代碼請參考
unp/program/icmp/traceroute
// 預設 30 跳
int done = 0;
for (ttl = 1:30 && !done) {
for (i = 0; i < 3; ++i) {
// 重複發 3 次
// 1. 發送 UDP 封包給目标主機
// 2. 接收 ICMP 封包
// 3.
if (// 沒有收到 icmp 封包) {
printf(" *");
}
else if (icmp->type= 11 && icmp->code == 0) {
// 逾時封包
// 列印發送者(src) ip 位址以及 rtt
}
else if (icmp->type= 3 && icmp->code == 3) {
// 端口不可達封包
// 已經到達目标主機,列印 ip 位址以及 rtt
done = 1; // 可以提前結束
3. 實驗
tracert 程式接收指令行參數
-m hops
,用 m 來指定最大跳數。來看一下跟蹤到主機 mars 的路徑:
圖6 運作結果
出現 * 号是因為有些中間路由器不産生 ICMP 逾時封包(該功能被關閉)。
下面是使用自帶的 traceroute 指令得出的結果:
4. 總結
- 掌握 traceroute 程式的原理
- 編寫 traceroute 程式