package starnet import ( "bytes" "encoding/binary" "net" "time" ) type ICMP struct { Type uint8 Code uint8 CheckSum uint16 Identifier uint16 SequenceNum uint16 } func getICMP(seq uint16) ICMP { icmp := ICMP{ Type: 8, Code: 0, CheckSum: 0, Identifier: 0, SequenceNum: seq, } var buffer bytes.Buffer binary.Write(&buffer, binary.BigEndian, icmp) icmp.CheckSum = checkSum(buffer.Bytes()) buffer.Reset() return icmp } func sendICMPRequest(icmp ICMP, destAddr *net.IPAddr, timeout time.Duration) (PingResult, error) { var res PingResult res.RemoteIP = destAddr.String() conn, err := net.DialIP("ip:icmp", nil, destAddr) if err != nil { return res, err } defer conn.Close() var buffer bytes.Buffer binary.Write(&buffer, binary.BigEndian, icmp) if _, err := conn.Write(buffer.Bytes()); err != nil { return res, err } tStart := time.Now() conn.SetReadDeadline((time.Now().Add(timeout))) recv := make([]byte, 1024) res.RecvCount, err = conn.Read(recv) if err != nil { return res, err } tEnd := time.Now() res.Duration = tEnd.Sub(tStart) return res, err } func checkSum(data []byte) uint16 { var ( sum uint32 length int = len(data) index int ) for length > 1 { sum += uint32(data[index])<<8 + uint32(data[index+1]) index += 2 length -= 2 } if length > 0 { sum += uint32(data[index]) } sum += (sum >> 16) return uint16(^sum) } type PingResult struct { Duration time.Duration RecvCount int RemoteIP string } func Ping(ip string, seq int, timeout time.Duration) (PingResult, error) { var res PingResult ipAddr, err := net.ResolveIPAddr("ip", ip) if err != nil { return res, err } icmp := getICMP(uint16(seq)) return sendICMPRequest(icmp, ipAddr, timeout) } func IsIpPingable(ip string, timeout time.Duration, retryLimit int) bool { for i := 0; i < retryLimit; i++ { _, err := Ping(ip, 29, timeout) if err != nil { continue } return true } return false }