package ping import ( "log" "strconv" "strings" ) type PingOptions struct { Retry int Interval int Deadline int } type Response struct { TTL int Time float32 } type Summary struct { SendCount int ReceiveCount int LossPercent float32 MinTime float32 MaxTime float32 AvgTime float32 } type PingResult struct { Responses map[int]*Response Summary *Summary } func (o *PingOptions) Validate() { if 0 >= o.Retry { o.Retry = 1 } if 0 >= o.Interval { o.Interval = 1 } if 0 >= o.Deadline { o.Deadline = 1 } } // $ ping 192.168.1.1 -c 7 -i 1 -w 0.3 // PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data. // 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.325 ms // 64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.344 ms // 64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.163 ms // 64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=0.180 ms // --- 192.168.1.1 ping statistics --- // 7 packets transmitted, 4 received, 42% packet loss, time 6010ms // rtt min/avg/max/mdev = 0.163/0.253/0.344/0.082 ms func parseLinuxPing(output []byte) (*PingResult, error) { result := &PingResult{ Responses: make(map[int]*Response, 0), Summary: &Summary{}, } lines := strings.Split(string(output), "\n") LOOP: for _, line := range lines { log.Print(line) fields := strings.Fields(line) switch len(fields) { case 5: if "rtt" != fields[0] { continue LOOP } times := strings.Split(fields[3], "/") minTime, err := strconv.ParseFloat(times[0], 32) if nil != err { log.Print(err) continue LOOP } result.Summary.MinTime = float32(minTime) maxTime, err := strconv.ParseFloat(times[2], 32) if nil != err { log.Print(err) continue LOOP } result.Summary.MaxTime = float32(maxTime) avgTime, err := strconv.ParseFloat(times[1], 32) if nil != err { log.Print(err) continue LOOP } result.Summary.AvgTime = float32(avgTime) case 8: seqs := strings.Split(fields[4], "=") ttls := strings.Split(fields[5], "=") times := strings.Split(fields[6], "=") seq, err := strconv.Atoi(seqs[1]) if nil != err { log.Print(err) continue LOOP } ttl, err := strconv.Atoi(ttls[1]) if nil != err { log.Print(err) continue LOOP } _time, err := strconv.ParseFloat(times[1], 32) if nil != err { log.Print(err) continue LOOP } result.Responses[seq] = &Response{ TTL: ttl, Time: float32(_time), } case 10: sendCount, err := strconv.Atoi(fields[0]) if nil != err { log.Print(err) continue LOOP } result.Summary.SendCount = sendCount receiveCount, err := strconv.Atoi(fields[3]) if nil != err { log.Print(err) continue LOOP } result.Summary.ReceiveCount = receiveCount lossPercent, err := strconv.ParseFloat(strings.Replace(fields[5], "%", "", -1), 32) if nil != err { log.Print(err) continue LOOP } result.Summary.LossPercent = float32(lossPercent) } } return result, nil } // Windows 10 // Active code page: 437 // Pinging 192.168.1.1 with 32 bytes of data: // Reply from 192.168.1.1: bytes=32 time<1ms TTL=64 // Reply from 192.168.1.1: bytes=32 time<1ms TTL=64 // Reply from 192.168.1.1: bytes=32 time<1ms TTL=64 // Reply from 192.168.1.1: bytes=32 time<1ms TTL=64 // Reply from 192.168.1.1: bytes=32 time<1ms TTL=64 // Ping statistics for 192.168.1.1: // Packets: Sent = 5, Received = 5, Lost = 0 (0% loss), // Approximate round trip times in milli-seconds: // Minimum = 0ms, Maximum = 0ms, Average = 0ms // Active code page: 437 // Pinging www.google.com [216.58.221.164] with 32 bytes of data: // Reply from 216.58.221.164: bytes=32 time=37ms TTL=51 // Request timed out. // Reply from 216.58.221.164: bytes=32 time=38ms TTL=51 // Reply from 216.58.221.164: bytes=32 time=37ms TTL=51 // Reply from 216.58.221.164: bytes=32 time=37ms TTL=51 // Ping statistics for 216.58.221.164: // Packets: Sent = 5, Received = 4, Lost = 1 (20% loss), // Approximate round trip times in milli-seconds: // Minimum = 37ms, Maximum = 38ms, Average = 37ms func parseWindowsPing(output []byte) (*PingResult, error) { result := &PingResult{ Responses: make(map[int]*Response, 0), Summary: &Summary{}, } lines := strings.Split(string(output), "\n") seq := 1 LOOP: for _, line := range lines { log.Print(line) fields := strings.Fields(line) switch len(fields) { case 3: log.Print(fields) if "Request timed out." != line { continue LOOP } result.Responses[seq] = nil seq = seq + 1 case 6: if "Reply" != fields[0] || "from" != fields[1] { continue LOOP } times := strings.Replace(fields[4], "time", "", -1) times = strings.Replace(times, "<", "", -1) times = strings.Replace(times, "=", "", -1) times = strings.Replace(times, "ms", "", -1) ttls := strings.Split(fields[5], "=") ttl, err := strconv.Atoi(ttls[1]) if nil != err { log.Print(err) continue LOOP } _time, err := strconv.ParseFloat(times, 32) if nil != err { log.Print(err) continue LOOP } result.Responses[seq] = &Response{ TTL: ttl, Time: float32(_time), } seq = seq + 1 case 9: log.Print(fields) if "Minimum" != fields[0] { continue LOOP } minTimes := strings.Replace(fields[2], "ms", "", -1) minTimes = strings.Replace(minTimes, ",", "", -1) minTime, err := strconv.ParseFloat(minTimes, 32) if nil != err { log.Print(err) continue LOOP } result.Summary.MinTime = float32(minTime) maxTimes := strings.Replace(fields[5], "ms", "", -1) maxTimes = strings.Replace(maxTimes, ",", "", -1) maxTime, err := strconv.ParseFloat(maxTimes, 32) if nil != err { log.Print(err) continue LOOP } result.Summary.MaxTime = float32(maxTime) avgTimes := strings.Replace(fields[8], "ms", "", -1) avgTime, err := strconv.ParseFloat(avgTimes, 32) if nil != err { log.Print(err) continue LOOP } result.Summary.AvgTime = float32(avgTime) case 12: if "Packets:" != fields[0] { continue LOOP } sendCount, err := strconv.Atoi(strings.Replace(fields[3], ",", "", -1)) if nil != err { log.Print(err) continue LOOP } result.Summary.SendCount = sendCount receiveCount, err := strconv.Atoi(strings.Replace(fields[6], ",", "", -1)) if nil != err { log.Print(err) continue LOOP } result.Summary.ReceiveCount = receiveCount lossPercents := strings.Replace(fields[10], "(", "", -1) lossPercents = strings.Replace(lossPercents, "%", "", -1) lossPercent, err := strconv.ParseFloat(lossPercents, 32) if nil != err { log.Print(err) continue LOOP } result.Summary.LossPercent = float32(lossPercent) } } return result, nil } // $ ping 192.168.1.1 -c 5 -i 1 // PING 192.168.1.1 (192.168.1.1): 56 data bytes // 64 bytes from 192.168.1.1: icmp_seq=0 ttl=64 time=1.664 ms // 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.971 ms // 64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=3.934 ms // 64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=3.539 ms // 64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=3.690 ms // --- 192.168.1.1 ping statistics --- // 5 packets transmitted, 5 packets received, 0.0% packet loss // round-trip min/avg/max/stddev = 0.971/2.760/3.934/1.204 ms func parseDarwinPing(output []byte) (*PingResult, error) { result := &PingResult{ Responses: make(map[int]*Response, 0), Summary: &Summary{}, } lines := strings.Split(string(output), "\n") LOOP: for _, line := range lines { log.Print(line) fields := strings.Fields(line) switch len(fields) { case 5: if "round-trip" != fields[0] { continue LOOP } times := strings.Split(fields[3], "/") minTime, err := strconv.ParseFloat(times[0], 32) if nil != err { log.Print(err) continue LOOP } result.Summary.MinTime = float32(minTime) maxTime, err := strconv.ParseFloat(times[2], 32) if nil != err { log.Print(err) continue LOOP } result.Summary.MaxTime = float32(maxTime) avgTime, err := strconv.ParseFloat(times[1], 32) if nil != err { log.Print(err) continue LOOP } result.Summary.AvgTime = float32(avgTime) case 8: seqs := strings.Split(fields[4], "=") ttls := strings.Split(fields[5], "=") times := strings.Split(fields[6], "=") seq, err := strconv.Atoi(seqs[1]) if nil != err { log.Print(err) continue LOOP } ttl, err := strconv.Atoi(ttls[1]) if nil != err { log.Print(err) continue LOOP } _time, err := strconv.ParseFloat(times[1], 32) if nil != err { log.Print(err) continue LOOP } result.Responses[seq] = &Response{ TTL: ttl, Time: float32(_time), } case 9: sendCount, err := strconv.Atoi(fields[0]) if nil != err { log.Print(err) continue LOOP } result.Summary.SendCount = sendCount receiveCount, err := strconv.Atoi(fields[3]) if nil != err { log.Print(err) continue LOOP } result.Summary.ReceiveCount = receiveCount lossPercent, err := strconv.ParseFloat(strings.Replace(fields[6], "%", "", -1), 32) if nil != err { log.Print(err) continue LOOP } result.Summary.LossPercent = float32(lossPercent) } } return result, nil }