欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何使用golang實(shí)現(xiàn)traceroute

 更新時(shí)間:2023年04月21日 11:39:32   作者:水番丘山  
這篇文章主要介紹了如何使用golang實(shí)現(xiàn)traceroute,該工具在linux環(huán)境下的命令是traceroute或者tracepath,在windows下命令是tracert,本文給大家詳細(xì)講解需要的朋友可以參考下

Traceroute 概念

traceroute是一種網(wǎng)絡(luò)診斷工具,通過traceroute可以診斷出本機(jī)到目的地IP之間的路由情況,例如路由跳數(shù)、延遲、是否可達(dá)等信息。該工具在linux環(huán)境下的命令是traceroute或者tracepath,在windows下命令是tracert。

工作原理

traceroute在linux系列的操作系統(tǒng),默認(rèn)通過發(fā)送UDP請(qǐng)求到目的地IP,UDP的端口使用的是33434到33545之間。除了UDP的協(xié)議,可選用ICMP或者TCP(TCP SYN包)。使用33434到33534之間到端口是因?yàn)榇蟛糠謑inux系統(tǒng)的該范圍內(nèi)的端口是不可用的。正常情況下如果我們對(duì)一個(gè)目的地主機(jī)發(fā)起UDP請(qǐng)求,并且該端口不存在就會(huì)直接返回端口或者主機(jī)不可大的信息,這樣是無(wú)法獲取到中途的路由節(jié)點(diǎn)。此時(shí)需要引入一個(gè)TTL的概念。

TTL即Time-To-Live,更多的被理解為路由跳數(shù),該值存于IP頭,經(jīng)過路由轉(zhuǎn)發(fā)時(shí)會(huì)將該值減1,當(dāng)ttl值為0時(shí),路由就會(huì)回復(fù)一個(gè)ICMP消息"Time Exceeded",表示跳數(shù)已經(jīng)達(dá)到最大值,無(wú)法進(jìn)行轉(zhuǎn)發(fā)。

IPV4報(bào)文

IPV4報(bào)文對(duì)TTL解釋

TTL在ipv4和ipv6頭有不同的定義,在ipv4頭用8位來(lái)存該數(shù)值,且命名為“Time to Live”,而在ipv6的頭則叫做“Hop Limit”。

IPV6報(bào)文

IPV6對(duì)于HopLimit的解釋

不管是Time to Live還是Hop Limit,其實(shí)都是相同的邏輯,路由轉(zhuǎn)發(fā)一次就減1,并且該值為0時(shí)則無(wú)法轉(zhuǎn)發(fā)。

我們來(lái)看一下traceroute的發(fā)包過程:

traceroute發(fā)包過程

第一步:主機(jī)A往目的主機(jī)B發(fā)送UDP包,包頭需要設(shè)置TTL=1,并且設(shè)置目的端口為33434。
第二步:主機(jī)A的最近的路由A收到UDP包以后,將TTL減1,此時(shí)TTL=0,路由A就將該包丟棄,并且回復(fù)主機(jī)A一條ICMP信息:“Time Exceeded”。
第三步:主機(jī)A收到ICMP的消息以后即可記錄ICMP發(fā)送主機(jī)的地址,該地址就是路由IP,并且主機(jī)A設(shè)置TTL=2,再次發(fā)送UDP包到目的主機(jī)B的33434端口。
第四步:以此類推,直到TTL超過設(shè)置的最大值或者收到目的主機(jī)返回的消息時(shí)停止發(fā)包,這樣就得到了一個(gè)路由地址列表,同時(shí)也能拿到發(fā)送到路由之間的消息延遲,如果路由超過設(shè)定的時(shí)間內(nèi)沒有相應(yīng),則置該跳數(shù)的路由地址為“*”。

traceroute-go代碼實(shí)現(xiàn)

由于go語(yǔ)言是高級(jí)語(yǔ)言,將udp以及tcp的包頭都封裝完整,無(wú)法定制設(shè)置ttl。好在golang提供了syscall庫(kù),該庫(kù)提供依稀了linux下的函數(shù)調(diào)用,因此可以利用該包的方法達(dá)到設(shè)置ttl的目的。在1.4之前可以使用標(biāo)準(zhǔn)庫(kù)syscall,但因?yàn)樵搸?kù)已經(jīng)被棄用,可以使用golang.org/x/sys庫(kù),該庫(kù)是syscall的擴(kuò)展,提供更加豐富的系統(tǒng)調(diào)用方法。
有庫(kù)的支持,我們則需要了解一下C語(yǔ)言的知識(shí),即用C語(yǔ)言發(fā)送udp包和接受icmp的信息,因此這里需要涉及到幾個(gè)函數(shù):

socket函數(shù),創(chuàng)建一個(gè)socke的文件描述,用于發(fā)送udp以及接收icmp的消息,golang對(duì)應(yīng)的函數(shù)為func Socket(domain, typ, proto int) (fd int, err error)setsockopt函數(shù),該函數(shù)可以用于設(shè)定IP的頭信息,我們要設(shè)定TTL就是利用該函數(shù),同時(shí)該函數(shù)可以設(shè)定socket的請(qǐng)求或者接收消息的超時(shí)時(shí)間,golang對(duì)應(yīng)的函數(shù)為func SetsockoptInt(fd, level, opt int, value int) (err error)sendto函數(shù),用于發(fā)送udp消息,golang對(duì)應(yīng)的函數(shù)為func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error)recvfrom函數(shù),用于接收icmp消息,golang對(duì)應(yīng)的函數(shù)為func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error)

函數(shù)準(zhǔn)備好以后就可以開工編寫golang版本的traceroute庫(kù)了。

首先,創(chuàng)建sendSocket,用于發(fā)送UDP包,注意內(nèi)部的參數(shù) unix.IPPROTO_UDP表示使用ipv4的udp協(xié)議,這個(gè)與ipv6協(xié)議是有區(qū)別的,可以通過命令man socket查看函數(shù)說明,然后創(chuàng)建一個(gè)recvSocket的socket文件描述符,用于接收ICMP的消息,這里調(diào)用了函數(shù)SetsockoptTimeval,用于設(shè)定接收消息的超時(shí)時(shí)間。

然后在for循環(huán)內(nèi)循環(huán)發(fā)送udp消息并且接收icmp消息:

代碼中SetsockoptInt函數(shù)設(shè)定ipv4的頭TTL,初始化ttl=1,通過Sendto函數(shù)將消息發(fā)送到目的地址和目的端口,這里目的端口從33434開始,會(huì)在33434到33534區(qū)間內(nèi)循環(huán)。

發(fā)送消息以后,通過Recvfrom接收消息,此時(shí)會(huì)判斷接收消息是否報(bào)錯(cuò),如果報(bào)錯(cuò)則直接退出循環(huán)并結(jié)束traceroute操作;如果沒有報(bào)錯(cuò),則需要解析返回的ICMP消息,由于ipv4的Header包頭長(zhǎng)度最小是20字節(jié),最大是60字節(jié),會(huì)出現(xiàn)浮動(dòng),因此需要拿到實(shí)際的ipv4頭長(zhǎng)度,這里使用ipv4庫(kù)的ParseHeader函數(shù)解析拿到ipv4的包頭結(jié)構(gòu),然后將收到的消息截取ipHeader.Len長(zhǎng)度就得到我們的ICMP消息結(jié)構(gòu)體,拿到ICMP消息結(jié)構(gòu)以后既可以根據(jù)Type判定消息類型,由于我們只關(guān)注ICMPTypeTimeExceededICMPTypeDestinationUnreachable類型的消息,因此其他消息我們都會(huì)丟棄,并且如果收到的是ICMPTypeTimeExceeded,則需要將發(fā)送方的地址(路由地址)存下來(lái),并且將ttl+1,然后再次循環(huán)發(fā)送udp消息到目的地。
如果收到的ICMP消息類型是ICMPTypeDestinationUnreachable或者ttl超過了最大的ttl設(shè)定或者接受的的ICMP消息來(lái)自于目的地址,則結(jié)束發(fā)包,并輸出結(jié)果。

當(dāng)然,如果接收到報(bào)錯(cuò)的消息,該消息可能是路由不通或者發(fā)包超時(shí),因此我們需要將該跳的路由地址設(shè)置為“*”,同時(shí)判定重試次數(shù),以及是否超過了最大TTL。
最后每次循環(huán)都將目的端口值+1,并且超過了最大的端口33534是又從最小端口開始,保障端口范圍一直在33434到33534之間。

結(jié)果輸出:
以下是我們自己的程序結(jié)果輸出:

以下是系統(tǒng)自帶的traceroute輸出:

總結(jié)

traceroute工具原理不難,但要實(shí)現(xiàn)這個(gè)過程需要涉及到一些基本知識(shí),如ip的報(bào)文組成、udp、icmp協(xié)議的一些基本知識(shí),另外就是需要知道路由跳數(shù)的基本原理,通過實(shí)現(xiàn)這個(gè)過程也可以加深這些基礎(chǔ)知識(shí),同時(shí)是對(duì)這些知識(shí)的運(yùn)用。
完整代碼已經(jīng)上傳到github,地址為:https://github.com/Kseleven/traceroute-go,歡迎大家star,當(dāng)然如有紕漏或者講解不正確的地方,歡迎指正。

參考文獻(xiàn)

  1. ipv4 rfc 791
  2. ipv6 rfc 2460
  3. icmp rfc 792
  4. traceroute rfc 1393
  5. linux man page-traceroute
  6. traceroute wiki
  7. icmp wiki
  8. golang sys庫(kù)

到此這篇關(guān)于如何使用golang實(shí)現(xiàn)traceroute的文章就介紹到這了,更多相關(guān)golang實(shí)現(xiàn)traceroute內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 通過案例詳細(xì)聊聊Go語(yǔ)言的變量與常量

    通過案例詳細(xì)聊聊Go語(yǔ)言的變量與常量

    在任何一門現(xiàn)代的高級(jí)語(yǔ)言中,變量和常量都是它非常基礎(chǔ)的程序結(jié)構(gòu)的組成部分,下面這篇文章主要給大家介紹了關(guān)于如何通過案例詳細(xì)聊聊Go語(yǔ)言的變量與常量的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • Go代碼檢查工具golangci-lint安裝使用方法

    Go代碼檢查工具golangci-lint安裝使用方法

    這篇文章主要給大家介紹了關(guān)于Go代碼檢查工具golangci-lint安裝使用的相關(guān)資料,golangci-lint用于許多開源項(xiàng)目中,比如kubernetes、Prometheus、TiDB等都使用golangci-lint用于代碼檢查,需要的朋友可以參考下
    2024-01-01
  • Go語(yǔ)言中websocket的使用demo分享

    Go語(yǔ)言中websocket的使用demo分享

    WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。這篇文章主要和大家分享了一個(gè)Go語(yǔ)言中websocket的使用demo,需要的可以參考一下
    2022-12-12
  • Go語(yǔ)言基礎(chǔ)函數(shù)基本用法及示例詳解

    Go語(yǔ)言基礎(chǔ)函數(shù)基本用法及示例詳解

    這篇文章主要為大家介紹了Go語(yǔ)言基礎(chǔ)函數(shù)基本用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2021-11-11
  • Golang庫(kù)插件注冊(cè)加載機(jī)制的問題

    Golang庫(kù)插件注冊(cè)加載機(jī)制的問題

    這篇文章主要介紹了Golang庫(kù)插件注冊(cè)加載機(jī)制,這里說的插件并不是指的golang原生的可以在buildmode中加載指定so文件的那種加載機(jī)制,需要的朋友可以參考下
    2022-03-03
  • golang如何獲得一個(gè)變量的類型

    golang如何獲得一個(gè)變量的類型

    這篇文章主要介紹了golang獲得一個(gè)變量類型的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2021-05-05
  • Go語(yǔ)言普通指針unsafe.Pointer?uintpt之間的關(guān)系及指針運(yùn)算

    Go語(yǔ)言普通指針unsafe.Pointer?uintpt之間的關(guān)系及指針運(yùn)算

    這篇文章主要為大家介紹了Go語(yǔ)言普通指針unsafe.Pointer?uintpt之間的關(guān)系及指針運(yùn)算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Golang執(zhí)行g(shù)o get私有庫(kù)提示

    Golang執(zhí)行g(shù)o get私有庫(kù)提示"410 Gone" 的問題及解決辦法

    這篇文章主要介紹了Golang執(zhí)行g(shù)o get私有庫(kù)提示”410 Gone“ 解決辦法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Go語(yǔ)言的文件操作代碼匯總

    Go語(yǔ)言的文件操作代碼匯總

    本文給大家匯總介紹了go語(yǔ)言中的文件操作的代碼,包括文件的讀寫,文件的新建打開和刪除等,希望對(duì)大家學(xué)習(xí)go語(yǔ)言能夠有所幫助
    2018-10-10
  • 深入理解golang中io.Writer接口的使用

    深入理解golang中io.Writer接口的使用

    io 是一個(gè) Golang 標(biāo)準(zhǔn)庫(kù)包,它為圍繞輸入和輸出的許多操作和用例定義了靈活的接口,這篇文章主要為大家介紹了Go中Writer接口的使用,需要的可以參考下
    2023-10-10

最新評(píng)論