在 Go 中实现 ICMP ping

发布于 2024-09-03 15:08:50 字数 61 浏览 4 评论 0原文

是否可以在 Go 中实现 ICMP ping?另一种方法是分叉一个“ping”进程,但我宁愿用 Go 编写。

Is it possible to implement an ICMP ping in Go? The alternative is to fork a 'ping' process, but I'd rather write it in Go.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟若柳尘 2024-09-10 15:08:50

以下代码展示了如何使用原始套接字通过 IPv4 执行 ping(需要 root 权限):

package main

import (
    "log"
    "net"
    "os"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
)

const targetIP = "8.8.8.8"

func main() {
    c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    if err != nil {
        log.Fatalf("listen err, %s", err)
    }
    defer c.Close()

    wm := icmp.Message{
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff, Seq: 1,
            Data: []byte("HELLO-R-U-THERE"),
        },
    }
    wb, err := wm.Marshal(nil)
    if err != nil {
        log.Fatal(err)
    }
    if _, err := c.WriteTo(wb, &net.IPAddr{IP: net.ParseIP(targetIP)}); err != nil {
        log.Fatalf("WriteTo err, %s", err)
    }

    rb := make([]byte, 1500)
    n, peer, err := c.ReadFrom(rb)
    if err != nil {
        log.Fatal(err)
    }
    rm, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), rb[:n])
    if err != nil {
        log.Fatal(err)
    }
    switch rm.Type {
    case ipv4.ICMPTypeEchoReply:
        log.Printf("got reflection from %v", peer)
    default:
        log.Printf("got %+v; want echo reply", rm)
    }
}

基于此处找到的示例的代码: https://godoc.org/golang.org/x/net/icmp#PacketConn

要以非特权用户身份从 Linux 执行 ping 操作,请参阅 这篇文章

The following code shows how to perform a ping over IPv4 using a raw socket (requires root privs):

package main

import (
    "log"
    "net"
    "os"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
)

const targetIP = "8.8.8.8"

func main() {
    c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    if err != nil {
        log.Fatalf("listen err, %s", err)
    }
    defer c.Close()

    wm := icmp.Message{
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff, Seq: 1,
            Data: []byte("HELLO-R-U-THERE"),
        },
    }
    wb, err := wm.Marshal(nil)
    if err != nil {
        log.Fatal(err)
    }
    if _, err := c.WriteTo(wb, &net.IPAddr{IP: net.ParseIP(targetIP)}); err != nil {
        log.Fatalf("WriteTo err, %s", err)
    }

    rb := make([]byte, 1500)
    n, peer, err := c.ReadFrom(rb)
    if err != nil {
        log.Fatal(err)
    }
    rm, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), rb[:n])
    if err != nil {
        log.Fatal(err)
    }
    switch rm.Type {
    case ipv4.ICMPTypeEchoReply:
        log.Printf("got reflection from %v", peer)
    default:
        log.Printf("got %+v; want echo reply", rm)
    }
}

Code based on the example found here: https://godoc.org/golang.org/x/net/icmp#PacketConn

In order to ping from Linux as a non-privileged user, see this post

夜灵血窟げ 2024-09-10 15:08:50

目前,Go net 软件包不支持 ICMP Echo (Ping) 功能。

不支持发送 ICMP
回显请求。你必须添加
支持打包网。 ping

Currently, the ICMP Echo (Ping) function isn't supported in the Go net package.

There's no support for sending ICMP
echo requests. You'd have to add
support to package net. ping

白龙吟 2024-09-10 15:08:50

要在没有 root 要求的情况下执行此操作,您可以使用

package main

import (
    "fmt"
    "net"
    "os"
    "time"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
)

const target = "google.com"

func main() {
    for {
        time.Sleep(time.Second * 1)
        Ping(target)
    }
}

func Ping(target string) {
    ip, err := net.ResolveIPAddr("ip4", target)
    if err != nil {
        panic(err)
    }
    conn, err := icmp.ListenPacket("udp4", "0.0.0.0")
    if err != nil {
        fmt.Printf("Error on ListenPacket")
        panic(err)
    }
    defer conn.Close()

    msg := icmp.Message{
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff, Seq: 1,
            Data: []byte(""),
        },
    }
    msg_bytes, err := msg.Marshal(nil)
    if err != nil {
        fmt.Printf("Error on Marshal %v", msg_bytes)
        panic(err)
    }

    // Write the message to the listening connection
    if _, err := conn.WriteTo(msg_bytes, &net.UDPAddr{IP: net.ParseIP(ip.String())}); err != nil {
        fmt.Printf("Error on WriteTo %v", err)
        panic(err)
    }

    err = conn.SetReadDeadline(time.Now().Add(time.Second * 1))
    if err != nil {
        fmt.Printf("Error on SetReadDeadline %v", err)
        panic(err)
    }
    reply := make([]byte, 1500)
    n, _, err := conn.ReadFrom(reply)

    if err != nil {
        fmt.Printf("Error on ReadFrom %v", err)
        panic(err)
    }
    parsed_reply, err := icmp.ParseMessage(1, reply[:n])

    if err != nil {
        fmt.Printf("Error on ParseMessage %v", err)
        panic(err)
    }

    switch parsed_reply.Code {
    case 0:
        // Got a reply so we can save this
        fmt.Printf("Got Reply from %s\n", target)
    case 3:
        fmt.Printf("Host %s is unreachable\n", target)
        // Given that we don't expect google to be unreachable, we can assume that our network is down
    case 11:
        // Time Exceeded so we can assume our network is slow
        fmt.Printf("Host %s is slow\n", target)
    default:
        // We don't know what this is so we can assume it's unreachable
        fmt.Printf("Host %s is unreachable\n", target)
    }
}

To perform this without root requirement you can use

package main

import (
    "fmt"
    "net"
    "os"
    "time"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
)

const target = "google.com"

func main() {
    for {
        time.Sleep(time.Second * 1)
        Ping(target)
    }
}

func Ping(target string) {
    ip, err := net.ResolveIPAddr("ip4", target)
    if err != nil {
        panic(err)
    }
    conn, err := icmp.ListenPacket("udp4", "0.0.0.0")
    if err != nil {
        fmt.Printf("Error on ListenPacket")
        panic(err)
    }
    defer conn.Close()

    msg := icmp.Message{
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff, Seq: 1,
            Data: []byte(""),
        },
    }
    msg_bytes, err := msg.Marshal(nil)
    if err != nil {
        fmt.Printf("Error on Marshal %v", msg_bytes)
        panic(err)
    }

    // Write the message to the listening connection
    if _, err := conn.WriteTo(msg_bytes, &net.UDPAddr{IP: net.ParseIP(ip.String())}); err != nil {
        fmt.Printf("Error on WriteTo %v", err)
        panic(err)
    }

    err = conn.SetReadDeadline(time.Now().Add(time.Second * 1))
    if err != nil {
        fmt.Printf("Error on SetReadDeadline %v", err)
        panic(err)
    }
    reply := make([]byte, 1500)
    n, _, err := conn.ReadFrom(reply)

    if err != nil {
        fmt.Printf("Error on ReadFrom %v", err)
        panic(err)
    }
    parsed_reply, err := icmp.ParseMessage(1, reply[:n])

    if err != nil {
        fmt.Printf("Error on ParseMessage %v", err)
        panic(err)
    }

    switch parsed_reply.Code {
    case 0:
        // Got a reply so we can save this
        fmt.Printf("Got Reply from %s\n", target)
    case 3:
        fmt.Printf("Host %s is unreachable\n", target)
        // Given that we don't expect google to be unreachable, we can assume that our network is down
    case 11:
        // Time Exceeded so we can assume our network is slow
        fmt.Printf("Host %s is slow\n", target)
    default:
        // We don't know what this is so we can assume it's unreachable
        fmt.Printf("Host %s is unreachable\n", target)
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文