This repository was archived by the owner on Apr 20, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibping.go
139 lines (118 loc) · 4.08 KB
/
libping.go
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
Package libping provide the ability to send ICMP packets easily.
*/
package libping
import (
"bytes"
"net"
"os"
"time"
)
const (
ICMP_ECHO_REQUEST = 8
ICMP_ECHO_REPLY = 0
)
// The struct Response is the data returned by Pinguntil.
type Response struct {
Delay time.Duration
Error error
Destination string
Seq int
Readsize int
Writesize int
}
func makePingRequest(id, seq, pktlen int, filler []byte) []byte {
p := make([]byte, pktlen)
copy(p[8:], bytes.Repeat(filler, (pktlen-8)/len(filler)+1))
p[0] = ICMP_ECHO_REQUEST // type
p[1] = 0 // code
p[2] = 0 // cksum
p[3] = 0 // cksum
p[4] = uint8(id >> 8) // id
p[5] = uint8(id & 0xff) // id
p[6] = uint8(seq >> 8) // sequence
p[7] = uint8(seq & 0xff) // sequence
// calculate icmp checksum
cklen := len(p)
s := uint32(0)
for i := 0; i < (cklen - 1); i += 2 {
s += uint32(p[i+1])<<8 | uint32(p[i])
}
if cklen&1 == 1 {
s += uint32(p[cklen-1])
}
s = (s >> 16) + (s & 0xffff)
s = s + (s >> 16)
// place checksum back in header; using ^= avoids the
// assumption the checksum bytes are zero
p[2] ^= uint8(^s & 0xff)
p[3] ^= uint8(^s >> 8)
return p
}
func parsePingReply(p []byte) (id, seq, code int) {
id = int(p[24])<<8 | int(p[25])
seq = int(p[26])<<8 | int(p[27])
code = int(p[21])
return
}
// Pingonce send one ICMP echo packet to the destination, and return the latency.
// The function is made to be simple. Simple request, simple reply.
func Pingonce(destination string) (time.Duration, error) {
response := make(chan Response)
go Pinguntil(destination, 1, response, time.Second)
answer := <-response
return answer.Delay, answer.Error
}
// Pinguntil will send ICMP echo packets to the destination until the counter is reached, or forever if the counter is set to 0.
// The replies are given in the Response format.
// You can also adjust the delay between two ICMP echo packets with the variable delay.
func Pinguntil(destination string, count int, response chan Response, delay time.Duration) {
raddr, err := net.ResolveIPAddr("ip", destination)
if err != nil {
response <- Response{Delay: 0, Error: err, Destination: destination, Seq: 0}
close(response)
return
}
ipconn, err := net.Dial("ip:icmp", raddr.IP.String())
if err != nil {
response <- Response{Delay: 0, Error: err, Destination: raddr.IP.String(), Seq: 0}
close(response)
return
}
sendid := os.Getpid() & 0xffff
pingpktlen := 64
seq := 0
var elapsed time.Duration = 0
for ; seq < count || count == 0; seq++ {
elapsed = 0
if seq > 65535 { // The two bytes for seq. Don't overflow!
seq = 0
}
sendpkt := makePingRequest(sendid, seq, pingpktlen, []byte("Go Ping"))
start := time.Now()
writesize, err := ipconn.Write(sendpkt)
if err != nil || writesize != pingpktlen {
response <- Response{Delay: 0, Error: err, Destination: raddr.IP.String(), Seq: seq, Writesize: writesize, Readsize: 0}
time.Sleep(delay)
continue
}
ipconn.SetReadDeadline(time.Now().Add(time.Second * 1)) // 1 second
resp := make([]byte, 1024)
for {
readsize, err := ipconn.Read(resp)
elapsed = time.Now().Sub(start)
rid, rseq, rcode := parsePingReply(resp)
if err != nil {
response <- Response{Delay: 0, Error: err, Destination: raddr.IP.String(), Seq: seq, Writesize: writesize, Readsize: readsize}
break
} else if rcode != ICMP_ECHO_REPLY || rseq != seq || rid != sendid {
continue
} else {
response <- Response{Delay: elapsed, Error: err, Destination: raddr.IP.String(), Seq: seq, Writesize: writesize, Readsize: readsize}
break
}
}
time.Sleep(delay - elapsed)
}
close(response)
}