118 lines
2.1 KiB
Go
118 lines
2.1 KiB
Go
|
|
package ping
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"golang.org/x/net/icmp"
|
|
"golang.org/x/net/ipv4"
|
|
)
|
|
|
|
type ICMPPinger struct {
|
|
timeout time.Duration
|
|
}
|
|
|
|
func NewICMPPinger(timeout time.Duration) *ICMPPinger {
|
|
return &ICMPPinger{timeout: timeout}
|
|
}
|
|
|
|
func (p *ICMPPinger) Ping(host string, count int) (*PingResult, error) {
|
|
dst, err := net.ResolveIPAddr("ip4", host)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve host %s: %v", host, err)
|
|
}
|
|
|
|
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create ICMP connection: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
result := &PingResult{
|
|
Host: host,
|
|
PacketsSent: count,
|
|
StartTime: time.Now(),
|
|
}
|
|
|
|
var totalRTT time.Duration
|
|
var minRTT, maxRTT time.Duration
|
|
|
|
for i := 0; i < count; i++ {
|
|
message := &icmp.Message{
|
|
Type: ipv4.ICMPTypeEcho,
|
|
Code: 0,
|
|
Body: &icmp.Echo{
|
|
ID: os.Getpid() & 0xffff,
|
|
Seq: i + 1,
|
|
Data: []byte("Hello, World!"),
|
|
},
|
|
}
|
|
|
|
data, err := message.Marshal(nil)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
start := time.Now()
|
|
_, err = conn.WriteTo(data, dst)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
err = conn.SetReadDeadline(time.Now().Add(p.timeout))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
reply := make([]byte, 1500)
|
|
_, peer, err := conn.ReadFrom(reply)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
rtt := time.Since(start)
|
|
|
|
// Parse the reply
|
|
rm, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply, reply)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
switch rm.Type {
|
|
case ipv4.ICMPTypeEchoReply:
|
|
if peer.String() == dst.String() {
|
|
result.PacketsRecv++
|
|
totalRTT += rtt
|
|
|
|
if result.PacketsRecv == 1 || rtt < minRTT {
|
|
minRTT = rtt
|
|
}
|
|
if result.PacketsRecv == 1 || rtt > maxRTT {
|
|
maxRTT = rtt
|
|
}
|
|
|
|
result.RTTs = append(result.RTTs, rtt)
|
|
}
|
|
}
|
|
|
|
if i < count-1 {
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
|
|
result.EndTime = time.Now()
|
|
result.PacketLoss = float64(count-result.PacketsRecv) / float64(count) * 100
|
|
|
|
if result.PacketsRecv > 0 {
|
|
result.MinRTT = minRTT
|
|
result.MaxRTT = maxRTT
|
|
result.AvgRTT = totalRTT / time.Duration(result.PacketsRecv)
|
|
result.Success = true
|
|
}
|
|
|
|
return result, nil
|
|
}
|