在Go中实现ICMP ping

huangapple go评论93阅读模式
英文:

Implementing ICMP ping in Go

问题

在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.

答案1

得分: 13

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

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

答案2

得分: 5

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

> 没有支持发送ICMP echo请求的功能。您需要在net包中添加支持。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

答案3

得分: 2

要在不需要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)
	}

	// 将消息写入监听连接
	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:
		// 收到回复,可以保存
		fmt.Printf("从 %s 收到回复\n", target)
	case 3:
		fmt.Printf("主机 %s 不可达\n", target)
		// 鉴于我们不希望无法访问google,我们可以假设我们的网络不可用
	case 11:
		// 超时,可以假设我们的网络很慢
		fmt.Printf("主机 %s 很慢\n", target)
	default:
		// 我们不知道这是什么,所以可以假设它是不可达的
		fmt.Printf("主机 %s 不可达\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)
}
}

huangapple
  • 本文由 发表于 2010年5月30日 08:46:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/2937123.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定