天天看點

98-traceroute 程式

traceroute 程式可以讓我們看到 IP 資料報從一台主機傳到另一台主機所經過的路由,該程式最早由 Van Jacobson 實作。

當然我們不可能原汁原味的模仿一遍 traceroute 程式,在這裡,我們隻需要把最關鍵的功能實作出來就算完成任務。

1. 牛刀小試

1.1 仿真環境測試

這裡使用 GNS3 軟體來模拟實驗,該工程位于路徑​

​unp/protocol/gns3/demo01​

​下。拓撲圖如圖 1 所示。

98-traceroute 程式

圖1 仿真環境拓撲圖

  • 在 PC1 上執行 traceroute,跟蹤 PC1 到 PC3 的路徑
98-traceroute 程式

圖2 PC1 到 PC3 的路徑

  • 在 PC3 上執行 traceroute,跟蹤 PC3 到 PC1 的路徑
98-traceroute 程式

圖3 PC3 到 PC1 的路徑

1.2 真實網絡測試

  • 程式路徑

如果你已經 clone 過這個代碼了,請使用 ​

​git pull​

​​ 更新一下。本節程式所使用的程式路徑是 ​

​unp/program/icmp/traceroute​

​.

  • 運作結果
98-traceroute 程式

圖4 檢視到 192.168.165.1 這台路由器的路徑

上面的 tracert 是我們自己寫的程式,下面的 traceroute 是系統自帶的程式。可以看到,分别經過了 4 跳就到達了目的地。圖 5 是該網絡拓撲圖。

98-traceroute 程式

圖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 的路徑:

98-traceroute 程式

圖6 運作結果

出現 * 号是因為有些中間路由器不産生 ICMP 逾時封包(該功能被關閉)。

下面是使用自帶的 traceroute 指令得出的結果:

98-traceroute 程式

4. 總結

  • 掌握 traceroute 程式的原理
  • 編寫 traceroute 程式