tracert 命令与基于 Go 的简单实现

在网络诊断中,我们常常需要了解数据包从源主机到目标主机所经过的路径,以便定位网络问题。Windows 下的 tracert 就是一种常用的工具,它通过发送一系列带有特殊 TTL(Time To Live)值的 ICMP 数据包,并分析返回的 ICMP 报文,逐步揭示数据包途径的每一个路由器或网关,以及每个节点的响应时间和丢包情况,帮助我们诊断网络延迟和连接问题。

步骤 www.cqzlsb.com

  1. 发送 ICMP Echo Request 数据包: tracert 程序会向目标主机发送一系列 ICMP Echo Request (ping) 数据包。
  2. 设置递增的 TTL 值: 对于每个数据包,tracert 会将 IP 头部的 TTL 字段设置为一个递增的值,通常从 1 开始。
  3. 路由器递减 TTL: 当数据包经过网络上的每个路由器时,路由器会将 TTL 值减 1。
  4. TTL 到达 0: 当 TTL 值达到 0 时,路由器会丢弃该数据包,并向发送方发送一个 ICMP Time Exceeded (类型 11,代码 0) 消息。
  5. ICMP Time Exceeded 消息包含路由器信息: 这个 ICMP Time Exceeded 消息包含了丢弃数据包的路由器的 IP 地址。
  6. tracert 记录路由器信息: tracert 程序会接收 ICMP Time Exceeded 消息,并记录下该路由器的 IP 地址和往返时间 (RTT)。
  7. 重复步骤 1-6,直到到达目标主机: tracert 会不断增加 TTL 值,并重复发送数据包,直到其中一个数据包到达目标主机。
  8. 目标主机回应 ICMP Echo Reply: 当数据包到达目标主机时,目标主机将回应一个 ICMP Echo Reply (类型 0,代码 0) 消息。
  9. tracert 结束: tracert 程序接收到 ICMP Echo Reply 消息后,就知道已经到达目标主机,并结束追踪过程。

基于 Go 的 tracert 命令简单实现

这里使用了 golang.org/x/net/icmp 扩展库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
func Tracert(addr string) error {
    timeout := time.Second * 10

    conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    if err != nil {
        return fmt.Errorf("error listening for ICMP packets: %w", err)
    }
    defer conn.Close()

    destinationAddress, err := net.ResolveIPAddr("ip4", addr)
    if err != nil {
        return fmt.Errorf("error resolving hostname: %w", err)
    }
    fmt.Printf("Traceroute to '%s' [%s]\n", addr, destinationAddress.IP)

    message := icmp.Message{
        Type: ipv4.ICMPTypeEcho,
        Code: 0,
        Body: &icmp.Echo{
            ID:   os.Getpid() & 0xffff,
            Data: []byte("hello"),
        },
    }

    for ttl := 1; ttl <= 30; ttl++ {
        fmt.Printf("%d  ", ttl)

        message.Body.(*icmp.Echo).Seq = ttl

        messageBytes, err := message.Marshal(nil)
        if err != nil {
            return fmt.Errorf("error marshaling ICMP message: %w", err)
        }

        if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
            return fmt.Errorf("error setting TTL: %w", err)
        }

        start := time.Now()
        if _, err := conn.WriteTo(messageBytes, destinationAddress); err != nil {
            return fmt.Errorf("error sending ICMP packet: %w", err)
        }

        responseBytes := make([]byte, 1500)
        conn.SetReadDeadline(time.Now().Add(timeout))
        n, remoteAddress, err := conn.ReadFrom(responseBytes)
        if err != nil {
            if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
                fmt.Println("* (Timeout)")
            } else {
                fmt.Println("* (Error)")
            }
            continue
        }

        duration := time.Since(start)
        responseMessage, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), responseBytes[:n])
        if err != nil {
            return fmt.Errorf("error parsing ICMP message: %w", err)
        }

        switch responseMessage.Type {
        case ipv4.ICMPTypeTimeExceeded:
            fmt.Printf("%v  %v ms\n", remoteAddress, duration.Milliseconds())
        case ipv4.ICMPTypeEchoReply:
            fmt.Printf("%v  %v ms\n", remoteAddress, duration.Milliseconds())
            fmt.Println("Traceroute Complete.")
            return nil
        default:
            return fmt.Errorf("unexpected ICMP message type: %v, code: %v", responseMessage.Type, responseMessage.Code)
        }
    }

    return nil
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值