util-go/net/ping/ping.go

467 lines
11 KiB
Go
Raw Normal View History

2018-09-17 11:05:12 +00:00
package ping
import (
"log"
"strconv"
"strings"
)
2018-09-17 12:45:26 +00:00
type Option interface {
2018-09-17 15:09:04 +00:00
GetRetry() int
GetInterval() int
GetDeadline() int
2018-09-17 12:45:26 +00:00
Validate()
2018-09-17 11:05:12 +00:00
}
2018-09-17 12:45:26 +00:00
type Response interface {
2018-09-17 15:09:04 +00:00
GetTTL() int
GetTime() float32
GetError() string
2018-09-17 11:05:12 +00:00
}
2018-09-17 12:45:26 +00:00
type Summary interface {
2018-09-17 15:09:04 +00:00
GetSendCount() int
GetReceiveCount() int
GetLossPercent() float32
GetMinTime() float32
GetMaxTime() float32
GetAvgTime() float32
2018-09-17 11:05:12 +00:00
}
2018-09-17 12:45:26 +00:00
type Result interface {
2018-09-17 15:09:04 +00:00
GetResponses() map[int]Response
GetSummary() Summary
2018-09-17 12:45:26 +00:00
}
type PingOption struct {
2018-09-17 15:09:04 +00:00
Retry int `json:"retry,omitempty"`
Interval int `json:"interval,omitempty"`
Deadline int `json:"deadline,omitempty"`
2018-09-17 11:05:12 +00:00
}
2018-09-17 15:09:04 +00:00
func (o *PingOption) GetRetry() int {
return o.Retry
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (o *PingOption) GetInterval() int {
return o.Interval
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (o *PingOption) GetDeadline() int {
return o.Deadline
2018-09-17 12:45:26 +00:00
}
func (o *PingOption) Validate() {
2018-09-17 15:09:04 +00:00
if 0 >= o.Retry {
o.Retry = 1
2018-09-17 11:05:12 +00:00
}
2018-09-17 15:09:04 +00:00
if 0 >= o.Interval {
o.Interval = 1
2018-09-17 11:05:12 +00:00
}
2018-09-17 15:09:04 +00:00
if 0 >= o.Deadline {
o.Deadline = 1
2018-09-17 11:05:12 +00:00
}
}
2018-09-17 12:45:26 +00:00
type PingResponse struct {
2018-09-17 15:09:04 +00:00
TTL int `json:"ttl,omitempty"`
Time float32 `json:"time,omitempty"`
Error string `json:"error,omitempty"`
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (r *PingResponse) GetTTL() int {
return r.TTL
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (r *PingResponse) GetTime() float32 {
return r.Time
}
func (r *PingResponse) GetError() string {
return r.Error
2018-09-17 12:45:26 +00:00
}
type PingSummary struct {
2018-09-17 15:09:04 +00:00
SendCount int
ReceiveCount int
LossPercent float32
MinTime float32
MaxTime float32
AvgTime float32
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (s *PingSummary) GetSendCount() int {
return s.SendCount
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (s *PingSummary) GetReceiveCount() int {
return s.ReceiveCount
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (s *PingSummary) GetLossPercent() float32 {
return s.LossPercent
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (s *PingSummary) GetMinTime() float32 {
return s.MinTime
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (s *PingSummary) GetMaxTime() float32 {
return s.MaxTime
2018-09-17 12:45:26 +00:00
}
2018-09-17 15:09:04 +00:00
func (s *PingSummary) GetAvgTime() float32 {
return s.AvgTime
2018-09-17 12:45:26 +00:00
}
type PingResult struct {
responses map[int]Response
summary Summary
}
2018-09-17 15:09:04 +00:00
func (r *PingResult) GetResponses() map[int]Response {
2018-09-17 12:45:26 +00:00
return r.responses
}
2018-09-17 15:09:04 +00:00
func (r *PingResult) GetSummary() Summary {
2018-09-17 12:45:26 +00:00
return r.summary
}
2018-09-17 11:05:12 +00:00
// $ 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
2018-09-17 11:45:49 +00:00
// From 192.168.1.101 icmp_seq=1 Destination Host Unreachable
2018-09-17 11:05:12 +00:00
// --- 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
2018-09-17 12:45:26 +00:00
func parseLinuxPing(output []byte) (Result, error) {
2018-09-17 11:05:12 +00:00
result := &PingResult{
2018-09-17 12:45:26 +00:00
responses: make(map[int]Response, 0),
summary: &PingSummary{},
2018-09-17 11:05:12 +00:00
}
lines := strings.Split(string(output), "\n")
LOOP:
for _, line := range lines {
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
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).MinTime = float32(minTime)
2018-09-17 11:05:12 +00:00
maxTime, err := strconv.ParseFloat(times[2], 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).MaxTime = float32(maxTime)
2018-09-17 11:05:12 +00:00
avgTime, err := strconv.ParseFloat(times[1], 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).AvgTime = float32(avgTime)
2018-09-17 11:05:12 +00:00
case 8:
2018-09-17 11:45:49 +00:00
if "bytes" != fields[1] || "from" != fields[2] {
continue LOOP
}
2018-09-17 11:05:12 +00:00
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
}
2018-09-17 12:45:26 +00:00
result.responses[seq] = &PingResponse{
2018-09-17 15:09:04 +00:00
TTL: ttl,
Time: float32(_time),
2018-09-17 11:05:12 +00:00
}
case 10:
sendCount, err := strconv.Atoi(fields[0])
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).SendCount = sendCount
2018-09-17 11:05:12 +00:00
receiveCount, err := strconv.Atoi(fields[3])
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).ReceiveCount = receiveCount
2018-09-17 11:05:12 +00:00
lossPercent, err := strconv.ParseFloat(strings.Replace(fields[5], "%", "", -1), 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).LossPercent = float32(lossPercent)
2018-09-17 11:05:12 +00:00
}
}
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
2018-09-17 12:45:26 +00:00
func parseWindowsPing(output []byte) (Result, error) {
2018-09-17 11:05:12 +00:00
result := &PingResult{
2018-09-17 12:45:26 +00:00
responses: make(map[int]Response, 0),
summary: &PingSummary{},
2018-09-17 11:05:12 +00:00
}
lines := strings.Split(string(output), "\n")
seq := 1
LOOP:
for _, line := range lines {
fields := strings.Fields(line)
switch len(fields) {
case 3:
if "Request timed out." != line {
continue LOOP
}
2018-09-17 11:45:49 +00:00
// result.Responses[seq] = nil
2018-09-17 11:05:12 +00:00
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
}
2018-09-17 12:45:26 +00:00
result.responses[seq] = &PingResponse{
2018-09-17 15:09:04 +00:00
TTL: ttl,
Time: float32(_time),
2018-09-17 11:05:12 +00:00
}
seq = seq + 1
case 9:
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
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).MinTime = float32(minTime)
2018-09-17 11:05:12 +00:00
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
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).MaxTime = float32(maxTime)
2018-09-17 11:05:12 +00:00
avgTimes := strings.Replace(fields[8], "ms", "", -1)
avgTime, err := strconv.ParseFloat(avgTimes, 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).AvgTime = float32(avgTime)
2018-09-17 11:05:12 +00:00
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
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).SendCount = sendCount
2018-09-17 11:05:12 +00:00
receiveCount, err := strconv.Atoi(strings.Replace(fields[6], ",", "", -1))
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).ReceiveCount = receiveCount
2018-09-17 11:05:12 +00:00
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
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).LossPercent = float32(lossPercent)
2018-09-17 11:05:12 +00:00
}
}
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
2018-09-17 12:45:26 +00:00
func parseDarwinPing(output []byte) (Result, error) {
2018-09-17 11:05:12 +00:00
result := &PingResult{
2018-09-17 12:45:26 +00:00
responses: make(map[int]Response, 0),
summary: &PingSummary{},
2018-09-17 11:05:12 +00:00
}
lines := strings.Split(string(output), "\n")
LOOP:
for _, line := range lines {
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
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).MinTime = float32(minTime)
2018-09-17 11:05:12 +00:00
maxTime, err := strconv.ParseFloat(times[2], 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).MaxTime = float32(maxTime)
2018-09-17 11:05:12 +00:00
avgTime, err := strconv.ParseFloat(times[1], 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).AvgTime = float32(avgTime)
2018-09-17 11:05:12 +00:00
case 8:
2018-09-17 11:45:49 +00:00
if "bytes" != fields[1] || "from" != fields[2] {
continue LOOP
}
2018-09-17 11:05:12 +00:00
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
}
2018-09-17 12:45:26 +00:00
result.responses[seq] = &PingResponse{
2018-09-17 15:09:04 +00:00
TTL: ttl,
Time: float32(_time),
2018-09-17 11:05:12 +00:00
}
case 9:
sendCount, err := strconv.Atoi(fields[0])
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).SendCount = sendCount
2018-09-17 11:05:12 +00:00
receiveCount, err := strconv.Atoi(fields[3])
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).ReceiveCount = receiveCount
2018-09-17 11:05:12 +00:00
lossPercent, err := strconv.ParseFloat(strings.Replace(fields[6], "%", "", -1), 32)
if nil != err {
log.Print(err)
continue LOOP
}
2018-09-17 15:09:04 +00:00
result.summary.(*PingSummary).LossPercent = float32(lossPercent)
2018-09-17 11:05:12 +00:00
}
}
return result, nil
}