前言
從我面試的經驗來看,網易這個公司是很會深究的一個公司,會努力在一個點上將面試者問倒的公司,所以對於網易游戲的面經我不會傾向於簡單的羅列問題,而是對他們提出的問題從原理上進行深度的理解。正因為被他們問的很深,所以對於問的問題我有很深的印象,前面的兩篇博客分別是關於python的特性,關於python的部門還沒有完,后續還會再寫一些面試題目的,今天要說的是一個網絡程序——traceroute,面試官當時讓我講述traceroute的原理,這個在之前我知道他使用ICMP結合TTL來實現的,實現也是相當巧妙,今天的博文參考TCP/IP詳解卷一,來徹底的搞清楚這個程序。
正文
在Windows下,相同的功能的實現命令為:tracert,首先至關的感受下traceroute命令的效果。我執行了兩次tracerout百度的命令,結果如下:
根據上面的執行結果包括對於程序的名字traceroute相信沒有接觸過這個命令的人也可以大體猜出來這個命令的作用——路由跟蹤,即確定IP數據包訪問目標所經過的路徑,當然這些路徑指的是路由器。對於執行結果的每一行來說,比如3 2ms 1ms 1ms 10.6.17.1表示從本機出發的第三跳路由器地址為10.6.17.1,中間的三列時間顯示了RTT,即數據包到達此跳路由或主機再返回你的主機所需要的時間,單位是毫秒,為什么是三列呢?因為traceroute發送了三個獨立的數據包,統計出了每個RTT,而國內很多博客在這里存在誤區,認為三列分別代表最小,平均和最大時間,有的時間欄沒有具體的數字,只是一個星號,這個可能有多種多樣的原因,比如路由器禁止了ICMP數據包,traceroute程序本身就被用來發現網絡故障,如果從某跳開始所有的時間都成了星號,即超時,則網絡故障很有可能就出現在了這一跳。從上面兩次執行結果可以看到,traceroute命令執行的結果不是完全相同的,這個也是很容易理解的,畢竟網絡是動態時時變化的。
Traceroute程序使用ICMP報文和IP首部中的TTL字段,TTL由發送端初始化,當路由器接收到一份IP數據包,如果TTL字段是0或者1,路由器將該數據包丟棄,並給源主機發一個ICMP“超時”信息。按照這個基本的相應過程,可以猜想traceroute程序的完整過程,首先它發送一份TTL字段為1的IP數據報給目的主機,處理這個數據報的第一個路由器將TTL值減1,然后丟棄該數據報,並給源主機發送一個ICMP報文,這個報文包含了路由器的IP地址,這樣就得到了第一個路由器的地址,然后,traceroute發送一個TTL為2的數據報來得到第二個路由器的IP地址,繼續這個過程,直至這個數據報到達目的主機。按照這個原理我們也可以很好的解釋了上面tracert www.baidu.com的結果了,即所有顯示的13跳是從我到百度119.75.218.70這個服務器所經過的所有路由器的信息,而且由於網絡是動態變化的,路徑也可能會發生變化,所以如果追蹤同樣的目的IP,發現不同的路徑當屬於正常情況。
這里必須加一條分割線,因為這個知識我相信很多人在很早之前就知道了,但是面試官的面試重點顯然不在上面的知識點,如果你回答了上述過程,面試官肯定會接着問一個問題:目的主機在接收到TTL值為1的IP數據報是不會丟失的吧,這樣也不會產生一個超時的ICMP數據報文了,那么程序如何判斷是否已經到達目的主機了呢?
在Linux下,traceroute程序發送一個UDP數據報給目的主機,但它選擇一個不可能的值作為UDP端口號(大於30000),使目的主機的任何一個應用程序都不可能使用該端口,因此,當該數據報達到目的主機的時候,目的主機會產生一個“端口不可達”錯誤的ICMP報文,這樣,traceroute程序要做的就是區分接收到的ICMP報文是超時還是端口不可達,從而來區分是路由器還是目的主機。
下面進行實驗驗證,仍然用上面的例子,用wireshark抓取數據報來驗證。由於主機上有大量的數據報文,所以我們根據本地IP地址和百度服務器的IP地址進行數據報過濾,按照我的例子過濾條件應該為:ip.src192.168.99.200&&ip.dst119.75.218.70,首先整體看一個過濾之后的報文總體情況,如下圖:
其中淺色背景的表示本機構造的ICMP數據報文,深色的表示超時ICMP報文,如果細心的人就會發現:所發送的數據報的數量要大於路徑上的路由器的數量,這個上文也提到過,每一跳都會發送三個報文,同時相應報文的數量少於構造報文數量,因為有些路由器禁止相應ICMP數據報,只提供轉發功能,所有本地發出的構造ICMP數據報源IP都是本地,目的IP都是百度的IP地址,所有相應的超時ICMP數據報的目的IP都是本地,源IP地址分為為路由器的IP地址,首先來看一個本機構造的ICMP數據報文:
在這個報文中,可以注意他的TTL的值為1,同時他填充的負載為0,相同的報文會發送3次,接着再看這個報文的響應報文:
在這個響應報文中應該注意的點我都用紅色方框圈出來了。
關於最后確定是否達到目的主機則和具體的操作系統有關系,在Windows下,我猜測他可以通過正確的回應ICMP數據報得出已經到達目的主機,而在Linux下面,則是通過上文所說的原理實現的,附圖如下:
總結
關於traceroute的內容,如果在進行深究,還有很多很多,比如他可以通過ICMP,TCP實現,在Windows下面達到目的主機之后會停止發送traceroute數據報,但是在Linux下,會持續發送30個TTL不同的數據報,每個發送三個報文,在達到目的主機之前可以發送ICMP,TCP等,但是在達到之后,開始發送UDP數據報,目的端口從大於30000的某個值開始,沒發送一個數據報則加一,這個從上面的截圖也可以看出。
好公司在面試的時候都會揪住一個點問的很深很深,所以在自己的領域,將基礎知識一定要掌握的十分好。