-
Notifications
You must be signed in to change notification settings - Fork 3
/
icmp.go
106 lines (94 loc) · 2.44 KB
/
icmp.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
package scan
import (
"math/rand"
"net"
"os"
"time"
"github.com/devops-works/scan-exporter/metrics"
"github.com/rs/zerolog/log"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
// ping does an ICMP echo request to the target. We do not want it to be blocking,
// so every error is followed by a continue. This way, in the worst scanario, the
// ping is skipped.
func (t *target) ping(timeout time.Duration, pchan chan metrics.PingInfo) {
p, err := getDuration(t.icmpPeriod)
if err != nil {
log.Fatal().Err(err).Msgf("cannot parse duration %s", t.icmpPeriod)
}
ip, err := getIP()
if err != nil {
log.Fatal().Err(err)
}
// Randomize period to avoid listening override.
// The random time added will be between 1 and 1.5s
rand.Seed(time.Now().UnixNano())
n := rand.Intn(500) + 1000
randPeriod := p + (time.Duration(n) * time.Millisecond)
ticker := time.NewTicker(randPeriod)
for {
select {
case <-ticker.C:
// Create update variable
pinfo := metrics.PingInfo{
Name: t.name,
IP: t.ip,
}
// Configure and send ICMP packet
destAddr := &net.IPAddr{IP: net.ParseIP(t.ip)}
c, err := icmp.ListenPacket("ip4:icmp", ip)
if err != nil {
// If the address is busy, wait a little bit and retry
time.Sleep(timeout)
c, err = icmp.ListenPacket("ip4:icmp", ip)
if err != nil {
log.Error().Err(err).Msg("error sending ping request")
continue
}
}
defer c.Close()
m := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte(""),
},
}
data, err := m.Marshal(nil)
if err != nil {
log.Error().Err(err).Msg("error sending ping request")
continue
}
start := time.Now()
_, err = c.WriteTo(data, destAddr)
if err != nil {
log.Error().Err(err).Msg("error sending ping request")
continue
}
reply := make([]byte, 1500)
err = c.SetReadDeadline(time.Now().Add(timeout))
if err != nil {
log.Error().Err(err).Msg("error sending ping request")
continue
}
n, SourceIP, err := c.ReadFrom(reply)
// timeout
if err != nil {
pinfo.IsResponding = false
pchan <- pinfo
continue
}
// if anything is read from the connection it means that the host is alive
if destAddr.String() == SourceIP.String() && n > 0 {
pinfo.RTT = time.Since(start)
pinfo.IsResponding = true
pchan <- pinfo
} else {
pinfo.IsResponding = false
pchan <- pinfo
}
}
}
}