Files
tendrils/ping.go

90 lines
1.5 KiB
Go
Raw Normal View History

package tendrils
import (
"net"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
func (t *Tendrils) pingNode(node *Node) {
t.nodes.mu.RLock()
var ips []string
for _, iface := range node.Interfaces {
for ipStr := range iface.IPs {
ip := net.ParseIP(ipStr)
if ip != nil && ip.To4() != nil {
ips = append(ips, ipStr)
}
}
}
t.nodes.mu.RUnlock()
if len(ips) == 0 {
return
}
for _, ipStr := range ips {
reachable := t.pingIP(ipStr)
if reachable {
t.errors.ClearUnreachable(node, ipStr)
} else {
t.errors.SetUnreachable(node, ipStr)
}
}
}
func (t *Tendrils) pingIP(ipStr string) bool {
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
return false
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(500 * time.Millisecond))
ip := net.ParseIP(ipStr)
seq := uint16(time.Now().UnixNano() & 0xFFFF)
msg := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: int(seq),
Seq: 1,
Data: []byte("tendrils"),
},
}
msgBytes, err := msg.Marshal(nil)
if err != nil {
return false
}
_, err = conn.WriteTo(msgBytes, &net.IPAddr{IP: ip})
if err != nil {
return false
}
buf := make([]byte, 1500)
for {
n, peer, err := conn.ReadFrom(buf)
if err != nil {
return false
}
parsed, err := icmp.ParseMessage(1, buf[:n])
if err != nil {
continue
}
if parsed.Type == ipv4.ICMPTypeEchoReply {
if ipAddr, ok := peer.(*net.IPAddr); ok {
if ipAddr.IP.String() == ipStr {
return true
}
}
}
}
}