Traceroute是一個非常便利的網(wǎng)絡(luò)診斷工具。它可以輸出以下三個內(nèi)容:
1 網(wǎng)絡(luò)數(shù)據(jù)包的從源地址到目的地址的整個傳輸路徑。
2 傳輸路徑上的路由設(shè)備的信息(IP地址或者h(yuǎn)ostname)
3 網(wǎng)絡(luò)數(shù)據(jù)包在路由設(shè)備間的延時(Latency)
從這些功能可以看出,traceroute通??梢杂糜谂袛嗑W(wǎng)絡(luò)故障,檢測網(wǎng)絡(luò)傳輸路徑等場合。Traceroute現(xiàn)在基本隨Linux系統(tǒng)發(fā)行,所以使用起來非常方便。在Windows系統(tǒng)下,對應(yīng)的工具是TRACERT.
Traceroute的一個優(yōu)點是,它不需要你發(fā)送實際的數(shù)據(jù)到目的地址,就能幫你輸出整個網(wǎng)絡(luò)路徑(實際上還是要發(fā)送數(shù)據(jù),只是發(fā)送的不是你的實際數(shù)據(jù))。
在基于OpenFlow的SDN中,要實現(xiàn)traceroute功能,實際上就是要在SDN中實現(xiàn)traceroute所依賴的網(wǎng)絡(luò)功能。那么接下來看看traceroute所依賴的網(wǎng)絡(luò)功能有哪些?
在IP協(xié)議(Internet Protocol)中,TTL(Time To Live)是一個8bit的字段,IPv4協(xié)議中,協(xié)議頭有20個8bit的字段,TTL占第9個8bit;IPv6協(xié)議中,協(xié)議頭有40個8bit的字段,TTL占第8個8bit。因此,TTL在IP協(xié)議中,最大值是255,通常的默認(rèn)值是64。下圖是IPv4協(xié)議的字段。

TTL存在的意義是什么?我們假設(shè)我們的網(wǎng)絡(luò)系統(tǒng)中,每個路由設(shè)備都有默認(rèn)路由,我們發(fā)出一個以不存在的地址作為目的地址的IP數(shù)據(jù)包,那么這個數(shù)據(jù)包將永遠(yuǎn)在我們的網(wǎng)絡(luò)系統(tǒng)中轉(zhuǎn)發(fā)。把網(wǎng)絡(luò)系統(tǒng)看成Internet,那么隨著時間推移,Internet必然會被大量這樣的“永生”IP數(shù)據(jù)包淹沒。正是為了避免這個問題,IP協(xié)議中提出了TTL,當(dāng)TTL為0,IP數(shù)據(jù)包會被丟棄。
TTL被設(shè)計為IP數(shù)據(jù)包在Internet中最長的存活時間。但是實際上,每個轉(zhuǎn)發(fā)了IP數(shù)據(jù)包的設(shè)備,都需要將TTL減1,也就是說TTL等于IP數(shù)據(jù)包能經(jīng)歷的最大跳(hop)數(shù),而不是時間(秒數(shù))。所以,為了避免誤解,在IPv6中,將TTL改名為hop limit。為了省事,我們還是叫它TTL吧。
回過來看前面的描述,有兩個問題:
什么是hop?
Hop是指IP數(shù)據(jù)包傳輸過程中的一段路徑。當(dāng)IP數(shù)據(jù)包從一個網(wǎng)絡(luò)設(shè)備傳輸至另一個網(wǎng)絡(luò)設(shè)備,這可以認(rèn)為是一個hop(跳)。
IP數(shù)據(jù)包被誰丟棄了?
被路由設(shè)備丟棄了,根據(jù)RFC1812,路由設(shè)備在轉(zhuǎn)發(fā)IP數(shù)據(jù)包的時候,會將TTL減1,如果減完之后的結(jié)果是0,那么IP數(shù)據(jù)包會被丟棄。所以說,在IP網(wǎng)絡(luò)中,路由設(shè)備需要丟棄TTL為1的數(shù)據(jù)包。
路由設(shè)備在丟棄TTL為1的數(shù)據(jù)包之后,還會向數(shù)據(jù)包的源地址發(fā)送一個ICMP Time exceeded message(ICMP type 11),在這條信息中,路由設(shè)備會將自己的IP地址作為源地址。
總的來說traceroute就是基于TTL和路由設(shè)備的特性來實現(xiàn)的。
基本實現(xiàn)原理
根據(jù)前面的描述,只要發(fā)送一個IP數(shù)據(jù)包,將TTL設(shè)為1,就能收到第一個路由設(shè)備返回的ICMP TTL exceeded message。將TTL設(shè)為2,就能收到第二個路由設(shè)備的。以此類推,當(dāng)TTL大到一定數(shù)的時候……,IP數(shù)據(jù)包就被目的設(shè)備收到,并且目的設(shè)備會做出響應(yīng)。所以traceroute的原理,簡單來說,如下圖所示:

探測包(UDP)
Traceroute會發(fā)送什么樣的IP數(shù)據(jù)包?默認(rèn)是UDP數(shù)據(jù)包。除了前面說過的TTL,這個UDP數(shù)據(jù)包會包含:
源地址
目的地址
一個UDP端口,端口號在33434和33534之間,這個區(qū)間的端口號對UDP來說是無效的端口號。因此目的地址收到了這個UDP數(shù)據(jù)包,會返回ICMP UDP Port Unreachable(ICMP type 3)的信息。這么一個別致的返回信息,traceroute在收到了它之后,就知道網(wǎng)絡(luò)路徑探測該結(jié)束了。
實際的數(shù)據(jù)流
描述的差不多了,我們來看一個簡單拓?fù)湎聇raceroute背后的數(shù)據(jù)流吧。網(wǎng)絡(luò)拓?fù)洌?/p>

tcpdump抓包:
http://paste.ubuntu.com/23803412/
除了前面已經(jīng)描述過的,有三點需要注意。
每個TTL都發(fā)了三個探測包出來,這是為了對同一個hop獲得三次的延時數(shù)據(jù),使得結(jié)果更加客觀。
每次發(fā)出的探測包,UDP端口都不一樣。這是為了將返回的信息跟發(fā)出的信息進(jìn)行匹配。以計算IP數(shù)據(jù)包從發(fā)出到接收的延時(Latency)。在上面的示例中,這似乎沒有必要,因為探測包都是一發(fā)一收,串行執(zhí)行。那是因為我用的是簡版的traceroute,在高版本的traceroute中,默認(rèn)是并行發(fā)送16個探測包。來感受一下并行發(fā)送的凌亂感吧。
https://paste.ubuntu.com/23791252/
Traceroute收到了ICMP信息,都包含有內(nèi)層信息,內(nèi)層信息似乎有探測包的大部分信息。
探測包:IP (tos 0x0, ttl 1, id 2143, offset 0, flags [DF], proto UDP (17), length 46)10.0.0.10.33875 > 10.0.1.10.33435: UDP, length 18回復(fù)包:IP (tos 0xc0, ttl 64, id 15477, offset 0, flags [none], proto ICMP (1), length 74)10.0.0.1 > 10.0.0.10: ICMP time exceeded in-transit, length 54? ? IP (tos 0x0, ttl 1, id 2143, offset 0, flags [DF], proto UDP (17), length 46)? ? 10.0.0.10.33875 > 10.0.1.10.33435: UDP, length 18
根據(jù)RFC777,ICMP協(xié)議要求將產(chǎn)生ICMP error的原數(shù)據(jù)包的至少前28字節(jié)拷貝至ICMP error message的payload,所以我們能看到探測包的內(nèi)容。28個字節(jié)包括了20個字節(jié)的IP報頭,和8個字節(jié)的UDP報頭。
三種traceroute的實現(xiàn)方式
之前介紹的都是基于UDP協(xié)議的traceroute實現(xiàn),而實際中traceroute還可以基于ICMP和TCP協(xié)議。首先要說的是,這三種方式的基本實現(xiàn)原理是一樣的,都是基于TTL和路由設(shè)備返回的信息。區(qū)別是探測包不一樣。為什么會有三種實現(xiàn)方式?因為你的防火墻可能阻止了UDP,為了讓traceroute在這樣的環(huán)境下也能工作,才有了別的實現(xiàn)。
ICMP traceroute
與UDP traceroute的區(qū)別就在于:
探測包就是ICMP echo request
目的設(shè)備返回的就是ICMP echo reply
linux下的traceroute可以通過指定參數(shù)來用ICMP做traceroute,而windows下的tracert默認(rèn)就是用ICMP來做traceroute。
TCP traceroute
與UDP traceroute的區(qū)別在于:
探測方式是與tcp 80(默認(rèn))端口建立連接
目的設(shè)備的返回時連接成功,或者80端口關(guān)閉
即使目的設(shè)備返回連接成功,traceroute程序會立即斷開連接,因為沒有必要。
問題
這是個比較有意思的方向。前面說過,路由設(shè)備返回的ICMP time exceeded message會將探測包的前28個字節(jié)拷貝至ICMP的payload。那如果路由設(shè)備做了NAT(network address translation),會發(fā)生什么結(jié)果?為了把問題描述清楚,我搭了一個如下的網(wǎng)絡(luò)拓?fù)洌?/p>

我們來看看TTL=2的探測包是怎么發(fā)送的:
第一個路由器上觀察到的數(shù)據(jù):
IP (tos 0x0, ttl 2, id 6596, offset 0, flags [DF], proto UDP (17), length 46)20.0.0.12.51016 > 192.168.31.94.33438: UDP, length 18
第二個路由器上觀察到的數(shù)據(jù):
IP (tos 0x0, ttl 1, id 6596, offset 0, flags [DF], proto UDP (17), length 46)172.24.4.3.51016 > 192.168.31.94.33438: UDP, length 18
可以看到,同一個探測包(端口號,ID一致),在第一個路由器,TTL=2,第二個路由器TTL=1,這跟前面的描述一致。并且在第二個路由器上,探測包變成了由172.24.4.3發(fā)送了。這是因為第一個路由器對探測包做了源地址NAT。
那第二個路由器如何知道將ICMP time exceeded message返回給20.0.0.12?
外層NAT
我們來看看第二個路由器(上面那個)返回的ICMP time exceeded message。
IP (tos 0xc0, ttl 64, id 55039, offset 0, flags [none], proto ICMP (1), length 74)172.24.4.1 > 172.24.4.3: ICMP time exceeded in-transit, length 54? ? IP (tos 0x0, ttl 1, id 6596, offset 0, flags[DF], proto UDP (17), length 46)? ? 172.24.4.3.51016 > 192.168.31.94.33438: UDP, length 18
第二個路由器不關(guān)心數(shù)據(jù)包從哪來,只是將當(dāng)前它收到的數(shù)據(jù)包的前28字節(jié)拷貝到了ICMP payload。所以ICMP內(nèi)層數(shù)據(jù)是從172.24.4.3到192.168.31.94。
第一個路由器(下面那個),自然會對數(shù)據(jù)包做反向NAT,即將外層的目的地址轉(zhuǎn)換成20.0.0.12。所以,第一個路由器做完外層NAT之后,數(shù)據(jù)包應(yīng)該是這樣的:
IP (tos 0xc0, ttl 63, id 55039, offset 0, flags [none], proto ICMP (1), length 74)172.24.4.1 > 20.0.0.12: ICMP time exceeded in-transit, length 54? ? IP (tos 0x0, ttl 1, id 6596, offset 0, flags[DF], proto UDP (17), length 46)? ? 172.24.4.3.51016 > 192.168.31.94.33438: UDP,length 18
內(nèi)層NAT
但是實際上,第一個路由器收到的ICMP time exceeded message是這樣的:
IP (tos 0xc0, ttl 63, id 55039, offset 0, flags [none], proto ICMP (1), length 74)172.24.4.1 > 20.0.0.12: ICMP time exceeded in-transit, length 54? ? IP (tos 0x0, ttl 1, id 6596, offset 0, flags[DF], proto UDP (17), length 46)? ? 20.0.0.12.51016 > 192.168.31.94.33438: UDP,length 18
內(nèi)層的地址也被反向NAT了。也就是說對于ICMP time exceeded message,支持NAT的路由器需要同時對外層包和內(nèi)層包做反向NAT。這樣,對于源設(shè)備20.0.0.12,它感覺不到外面發(fā)生了什么,但是同時又能獲取各個路由設(shè)備的信息。
RFC5508對此作了明確的定義:
NAT Behavioral Requirements for ICMP
完整的tcpdump記錄在:
http://paste.ubuntu.com/23803870/
可以看到ICMP包在經(jīng)過NAT設(shè)備后,內(nèi)層數(shù)據(jù)也做了NAT轉(zhuǎn)換。如果不對內(nèi)層數(shù)據(jù)做NAT,ICMP包將會是個無效的數(shù)據(jù)包。
前面說的所有的都是基于IP協(xié)議棧,而OpenFlow沒有這個東西。那就意味著,如果要對基于OpenFlow的router實現(xiàn)traceroute,需要手動實現(xiàn)前面說到的功能。這包括:
路由轉(zhuǎn)發(fā)時對IP數(shù)據(jù)包的TTL減1
路由轉(zhuǎn)發(fā)時,對TTL=1的數(shù)據(jù)包丟棄
丟棄了TTL=1的數(shù)據(jù)包時,返回ICMP time exceeded message。
由于SDN中的虛擬路由器端口可能是虛擬的,所以要求SDN中的虛擬路由器響應(yīng)UDP和TCP請求,返回ICMP Port Unreachable。
如果router支持NAT功能,還需要對ICMP error message做內(nèi)層數(shù)據(jù)包NAT。
TTL減1
這個在OpenFlow中已經(jīng)支持這樣的action,可以在路由的時候?qū)TL進(jìn)行減1。
丟棄invalid TTL packet,并返回ICMP time exceeded message
從OpenFlow 1.2起,支持將TTL invalid packet上送至OpenFlow控制器,在控制器內(nèi),可以生成ICMP time exceeded message,并返回給送入端口。
虛擬路由器響應(yīng)UDP和TCP請求,返回ICMP Port Unreachable
匹配虛擬路由器的端口上的UDP和TCP請求,上送OpenFlow控制器,在控制器內(nèi),可以生成ICMP Port Unreachable message,并返回給送入端口。
對ICMP error message做內(nèi)層數(shù)據(jù)包NAT
匹配NAT請求中的ICMP error message,上送OpenFlow控制器,在控制器內(nèi)對內(nèi)層數(shù)據(jù)包做NAT,再送到相應(yīng)的輸出端口。
上面有關(guān)實現(xiàn)的描述比較簡單,具體的實現(xiàn)可以參考到我在Dragonflow項目的代碼。
Support traceroute in dragonflow network
本文轉(zhuǎn)載自:https://zhuanlan.zhihu.com/p/24982540