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.
Asked
Active
Viewed 1.4k times
3 Answers
13
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

IanB
- 2,642
- 22
- 25
-
1Right now the IANA number for ICMP (1) is not accessible via `iana.ProtocolICMP`, but it could be accessed via `ipv4.ICMPTypeEcho.Protocol()`. The package `golang.org/x/net/internal/iana` is internal, when used the go 1.8 compiler says: `use of internal package not allowed` cf.: https://godoc.org/golang.org/x/net/ipv4#ICMPType.Protocol – TPPZ Sep 05 '17 at 16:11
5
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

peterSO
- 158,998
- 31
- 281
- 276
-
2Now (since late May 2010), Go supports raw sockets(See the net.IPConn type) - meaning you can implement ping yourself - and there's a ping example at https://code.google.com/p/go/source/browse/src/pkg/net/ipraw_test.go – nos Jul 23 '10 at 20:41
-
1Link by @nos doesn't work anymore. New URL should be: https://golang.org/src/net/ipraw_test.go – TheHippo Apr 08 '15 at 14:46
2
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)
}
}

E_K
- 2,159
- 23
- 39
-
This does require the net.ping_group_range to be set to allow ICMP via command `sysctl -w net.ping_group_range="0 2147483647"` – Eric C Jul 26 '22 at 20:52