Code Monkey home page Code Monkey logo

ping's People

Contributors

adamkeesey avatar aliriegray avatar cemremengu avatar chenlujjj avatar chtjonas avatar clinta avatar glinton avatar hipska avatar hustclf avatar isi-lincoln avatar jraby avatar leewei05 avatar maddie avatar mapl avatar mchurichi avatar mem avatar natesales avatar pgollangi avatar rrcollier avatar scientiacoder avatar sparrc avatar ssoroka avatar stenya avatar superq avatar vvarp avatar webfrank avatar xiezhenye avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ping's Issues

Channel lockup in func (p *Pinger) recvICMP

I stumbled upon channel lockup, when trying to send received data, while either one of the following occurs in func (p *Pinger) run():

  • Stop has been called
  • Interval has been reached
  • We received all expected packets

This occurs, because run func is not receiving data from recvICMP any more if above conditions are true and recvICMP is still trying to send received data to run func via recv channel. I Used select with both recv channel and p.done channel.

I attached the diff patch.

github.com_sparrc_go-ping_ping.go.txt

ping.Statistics is not thread-safe

ping.Statistics docs says This can be run while the pinger is running or after it is finished
https://github.com/sparrc/go-ping/blob/4e5b6552494c8005c60de6c60b50ebaefc69e592/ping.go#L356-L359

But the only safe places to run ping.Statistics are in ping.OnRecv and ping.OnFinish.
There is no sync mechanics to protect fields like PacketsRecv, PacketsSent, Rtts while they are being updated in processPacket

Imagine your pinger.Run goroutine is rescheduled and put to sleep just before https://github.com/sparrc/go-ping/blob/4e5b6552494c8005c60de6c60b50ebaefc69e592/ping.go#L499 PacketsRecv is updated, but Rtts is not. And your another goroutine calls pinger.Statistics on the same pinger - it will get inconsistent data.

But there is more, as far as I know, gouroutines can be rescheduled in the middle of statement, like p.PacketsSent++ or append. Then call to Statistics will read corrupted data.

Options:

  1. Fix Statistics to be thread-safe
  2. Fix godoc on Statistics

Add adjusted timeout if count is set

Issue

Process hangs for 27h46m40s (default timeout) even if I set Count to 2 if pinging a host that is down.

Proposed solution

Automatically add an adjusted timeout if Count is set - like Count * Interval.

Would you consider a PR?

Latency added when pinging en parallel (goroutine)

I'm trying ping in parallel a hole C class (254 ip addresses) but I realize the I need to add a few delay between the creation of every goroutine or otherwise the latency of every ping increases.
¿any ideas what could this be?
Thank you

Stats Accuracy

Hi @sparrc, first of all thank you for package. Started using it and ran into an issue and wanted to run it by you in case I am not using it correctly.

I have created a small function as follows

func PingCheck(HostToPing string) *ping.Statistics {
    pinger, err := ping.NewPinger(HostToPing)
    
    if err != nil {
        log.Error(err)
    }

    pinger.OnRecv = func(pkt *ping.Packet) {
        log.Infof("%d bytes from %s: icmp_seq=%d time=%v\n",
            pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
    }

    pinger.OnFinish = func(stats *ping.Statistics) {
        log.Infof("\n--- %s ping statistics ---\n", stats.Addr)
        log.Infof("%d packets transmitted, %d packets received, %v%% packet loss\n",
            stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
        log.Infof("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
            stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
    }

    pinger.Count = 30
    pinger.Timeout = 1 * time.Second
    pinger.Interval = 1
    pinger.SetPrivileged(true)

    log.Infof("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())
    pinger.Run()

    stats := pinger.Statistics()
    return stats
}

Here's the code that calls it from a test case

time.AfterFunc(1*time.Second, func() { <-- time.AfterFunc spawns a new go routine, maybe the issue?
  stats = utils.PingCheck(tests.TestIp)
  injectionPacketLoss = stats.PacketLoss
  t.Logf("during injection: packetLoss: %.2f", injectionPacketLoss)
  assert.True(t, injectionPacketLoss >= 50)
}

During the duration of the ping test, I am introducing packet 50% packet loss. The library is returning the following results

--- 172.217.7.142 ping statistics ---       
INFO[2019-10-11T15:13:22Z] 30 packets transmitted, 1 packets received, 96.66666666666667% packet loss 
INFO[2019-10-11T15:13:22Z] round-trip min/avg/max/stddev = 5.806085ms/5.806085ms/5.806085ms/0s 

Linux Ping is returning results that I am expecting, which is about 50% packet loss. Trying to understand why the library is returning a much higher packet loss, i.e. 96.6% loss ?

Linux Ping

ping -c 30  172.217.7.142

PING 172.217.7.142 (172.217.7.142) 56(84) bytes of data.
64 bytes from 172.217.7.142: icmp_seq=2 ttl=48 time=5.41 ms
64 bytes from 172.217.7.142: icmp_seq=5 ttl=48 time=5.38 ms
64 bytes from 172.217.7.142: icmp_seq=7 ttl=48 time=5.40 ms
64 bytes from 172.217.7.142: icmp_seq=8 ttl=48 time=5.45 ms
64 bytes from 172.217.7.142: icmp_seq=9 ttl=48 time=5.46 ms
64 bytes from 172.217.7.142: icmp_seq=10 ttl=48 time=5.44 ms
64 bytes from 172.217.7.142: icmp_seq=11 ttl=48 time=5.44 ms
64 bytes from 172.217.7.142: icmp_seq=13 ttl=48 time=5.46 ms
64 bytes from 172.217.7.142: icmp_seq=15 ttl=48 time=5.63 ms
64 bytes from 172.217.7.142: icmp_seq=16 ttl=48 time=5.54 ms
64 bytes from 172.217.7.142: icmp_seq=17 ttl=48 time=5.62 ms
64 bytes from 172.217.7.142: icmp_seq=18 ttl=48 time=5.42 ms
64 bytes from 172.217.7.142: icmp_seq=21 ttl=48 time=5.61 ms
64 bytes from 172.217.7.142: icmp_seq=23 ttl=48 time=5.34 ms
64 bytes from 172.217.7.142: icmp_seq=25 ttl=48 time=5.40 ms
64 bytes from 172.217.7.142: icmp_seq=27 ttl=48 time=6.05 ms
64 bytes from 172.217.7.142: icmp_seq=28 ttl=48 time=5.39 ms
64 bytes from 172.217.7.142: icmp_seq=30 ttl=48 time=5.56 ms

--- 172.217.7.142 ping statistics ---
30 packets transmitted, 15 received, 50% packet loss, time 29194ms
rtt min/avg/max/mdev = 5.346/5.504/6.055/0.162 ms

Thank you in advance for any help you can provide.

panic observed while pinging an IP address

Hello,

While using the go-ping library we've encountered an edge case where the library panics. The following is the code to reproduce the issue:

 package main

 import (
     "log"
     "time"

     ping "github.com/sparrc/go-ping"
 )

 func main() {

     for {
         pinger, err := ping.NewPinger("www.google.com")
         if err != nil {
             panic(err)
         }
         pinger.Count = 3
         pinger.Timeout = time.Second * 5
         pinger.SetPrivileged(true)
         pinger.Run() // blocks until finished

         if pinger.Statistics().PacketsRecv <= 0 {
             log.Print("ip not responding to pings")
         }

         time.Sleep(2)
     }
 }

I've tried to dig into the stacktrace observed as shown below:

2017/07/18 04:50:30 http: panic serving 10.60.7.163:42758: runtime error: slice bounds out of range
goroutine 29197 [running]:
net/http.(*conn).serve.func1(0xc42009e700)
        /usr/local/go/src/net/http/server.go:1491 +0x12a
panic(0x8b2580, 0xc4200100d0)
        /usr/local/go/src/runtime/panic.go:458 +0x243
vendor/github.com/sparrc/go-ping.(*Pinger).processPacket(0xc420362000, 0xc42035c760, 0xc420200b68, 0x1)
vendor/github.com/sparrc/go-ping/ping.go:437 +0x3ab
vendor/github.com/sparrc/go-ping.(*Pinger).run(0xc420362000)
vendor/github.com/sparrc/go-ping/ping.go:310 +0x667
vendor/github.com/sparrc/go-ping.(*Pinger).Run(0xc420362000)

My reasoning is as follows:

  1. On line: https://github.com/sparrc/go-ping/blob/master/ping.go#L437 if pkt.Data doesn't have enough bytes inside, the hardcoded constant of timeSliceLength https://github.com/sparrc/go-ping/blob/master/ping.go#L63 is going to make it go out of range.
  2. This could be handled better if a check like this is performed:
    a) pkt.Data[] is verified before it's evaluated with timeSliceLength to avoid panic.
    b) Better validate if a pkt is of type *icmp.Echo before it's used. Is there something else besides m.Body.(type) that can reveal this information?

Any ideas what would be the best way to solve this? Let me know if you need any additional details in order to repro this. Happy to help.

Add packet timeout callback

I would like to be notified when a ping fails in a specified timeout.

Example I have a long running pinger hitting 8.8.8.8, whenever a probe does not get a response within 1 second, I would like a callback to fire informing me of the same, so my code can do something with that information.

It appears that the only way to do so currently is call Statistics() regularly and do some math to figure out if something lossy happened recently or not.

Is there a way to make Run() execute faster?

package main
import (
    "fmt"
    "time"
    "github.com/sparrc/go-ping"
)
func main() {
    host := "192.168.2.1"
    pinger, _ := ping.NewPinger( host )
    pinger.Count = 1
    start := time.Now()
    pinger.Run()
    end := time.Now()
    fmt.Println("elapsed:", end.Sub(start))
    fmt.Println(pinger.Statistics())
}

Output:

elapsed: 101.549586ms
&{1 1 0 192.168.2.1 192.168.2.1 [1.427179ms] 1.427179ms 1.427179ms 1.427179ms 0s}

I am wondering if there is a way to make the Run() function execute faster. The actual ping itself only take 1.43 ms, but the entire function takes 101 ms. This seems like a lot of overhead. Also, the call to time.Now() only takes about 980ns. I get similar results across Windows, Linux and MacOS.

How can the be sped up?

Count specifies packets received, not packets sent

Count on Unix ping specifies the number of packets to send, whereas in this library, it waits for Count received packets.

This means that you will see more than the desired number of packets sent if you get any packet loss, or your ping time is higher than your interval.

Replicable if you run with the arguments "-c 10 -i 1ms"; you will see more than 10 packets sent, 10 packets received.

Timeout prevents application exit

When sending an unsuccessful ping request, it is no longer possible to end an application in a proper fashion, i.e. Ctrl-C does not work. I suspect the timeout code does not end all goroutines properly.

Example code:

	pinger, err := ping.NewPinger(addr)
	if err != nil {
		log.Fatal(err)
	}
	pinger.Timeout = time.Seconds * 10
	pinger.Count = 3
	pinger.SetPrivileged(true)
	pinger.Run()
	stats := pinger.Statistics()
	log.Println(stats)

Run should return an error when setup fails

The issues with raw socket support means that if the caller does not set privileged mode correctly, or if the user runs the executable without the required capab, the Pinger.Run() method can return immediately without sending any ping packets.

There is currently no direct way of differentiating between these problems caused by an invalid environment versus a successful run, as there is no return value from Run. The code simply logs the error to stdout, which isn't very helpful.

As a workaround, you can either check that Statistics.PacketsSent is non-zero, or you can check for the invocation of the OnFinish handler, which won't get invoked if setting up the ICMP listener fails, although neither of these are ideal.

I think Run should return an error in these circumstances, although sadly this would break backwards compat on the interface.

go-ping does not work when SetPrivileged(false)

A bug was introduced in d046b24

The following code is responsible:

	// Check if reply from same ID
	body := m.Body.(*icmp.Echo)
	if body.ID != p.id {
		return nil
	}

This code breaks when SetPrivileged(false) because the kernel is responsible for setting body.ID:

So even if I set the ID in the code, the kernel's ping_table will set it based on the value in the ping_table.

Send Echo: &icmp.Echo{ID:1234, Seq:1, Data:[]uint8{0x31, 0x32, 0x33}}
Receive Echo: &icmp.Echo{ID:12655, Seq:1, Data:[]uint8{0x31, 0x32, 0x33}}

So checking the ID will fail due to the mismatching IDs.

Allow selection of IP address family

Currently the NewPinger() defaults to automatic "ip" network selection. It would be useful to be explicit about selecting IPv4 vs IPv6, Then the ping cmd can allow for flags -4 and -6 to enforce which mode to use.

The simplest way would be to change it to something like:

func NewPinger(n string, addr string) (*Pinger, error) {
  ipaddr, err := net.ResolveIPAddr(n, addr)

if you run many goroutine some goroutine will be a deadlock

i have study golang for only 3 weeks i can not solve this problem

  1. if someone can help me to find out the reason why my program will block;
  2. i do many goroutine it has some wrong result ,some ip can ping right but pinger.PacketsRecv = 0
    image

i tried to find the reason, and i got some message

var wg sync.WaitGroup
recv := make(chan *packet, 5)
defer close(recv)
wg.Add(1)
go p.recvICMP(conn, recv, &wg)
err := p.sendICMP(conn)
if err != nil {
	fmt.Println(err.Error())
}
    timeout := time.NewTicker(p.Timeout)
defer timeout.Stop()
interval := time.NewTicker(p.Interval)
defer interval.Stop()

for {
	select {
	case <-p.done:
		wg.Wait()
		return
	case <-timeout.C:
		close(p.done)
		wg.Wait()
		return
	case <-interval.C:
		if p.Count > 0 && p.PacketsSent >= p.Count {
			continue
		}
		err = p.sendICMP(conn)
		if err != nil {
			fmt.Println("FATAL: ", err.Error())
		}
	case r := <-recv:
		err := p.processPacket(r)
		if err != nil {
			fmt.Println("FATAL: ", err.Error())
		}
	}
	if p.Count > 0 && p.PacketsRecv >= p.Count {
		close(p.done)
		wg.Wait()
		return
	}
}

i need to ping almost 16000 server ,if i use more goroutine to do this , block happen frequently
i debug my code , i found the reason
in ping.go 406

`

defer wg.Done()
for {
select {
case <-p.done:
fmt.Println("ok")
return
default:
bytes := make([]byte, 512)
conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
var n, ttl int
var err error
if p.ipv4 {
var cm *ipv4.ControlMessage
n, cm, _, err = conn.IPv4PacketConn().ReadFrom(bytes)
if cm != nil {
ttl = cm.TTL
}
} else {
var cm *ipv6.ControlMessage
n, cm, _, err = conn.IPv6PacketConn().ReadFrom(bytes)
if cm != nil {
ttl = cm.HopLimit
}
}
if err != nil {
if neterr, ok := err.(*net.OpError); ok {
if neterr.Timeout() {
// Read timeout
continue
} else {
close(p.done)
return
}
}
}
fmt.Println(len(recv))
recv <- &packet{bytes: bytes, nbytes: n, ttl: ttl}
}
}

`

the wg.Wait() is blocked by p.recvICMP(conn, recv, &wg)
and in the func recvICMP i foud in the for cycle
recv <- &packet{bytes: bytes, nbytes: n, ttl: ttl}
it will block by send a msg to the channel recv
but the chan recv := make(chan *packet, 5) only 5 size
if i do more goroutine i found the size 5 is not enough, so it will be blocked because the full channel ,
ok in some coincidence time the main goroutine dose not do the comsume func
case r := <-recv,
it is do close(p.done) then do wg.Wait() block to wait recvICMP
but in recvICMP
case <-p.done: fmt.Println("ok") return
this code will not effective because the block send msg to channel recv,so will not do
defer wg.Done()
then they all block like a deadlock
i sorry to my poor english
i don't konw i say my question clear
now i just do this to solve recv := make(chan *packet, 100)
by the way do many goroutine it has some wrong result ,some ip can ping right but pinger.PacketsRecv = 0

Option to disable timeout

I'm working on a long-running ping prober and would like an option to disable the timeout.

I'm currently playing around with a replacement "Forever" channel function.

type Forever struct {
       C <-chan int
}

func forever() *Forever {
       C := make(chan int)
       return &Forever{C: C}
}

Not found [-I iface] parameter : Specific interface

Can you implement for all parameter like ping on linux :)

usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize]
[-g sweepminsize] [-h sweepincrsize] [-i wait]
[-l preload] [-M mask | time] [-m ttl] [-p pattern]
[-S src_addr] [-s packetsize] [-t timeout][-W waittime]
[-z tos] host
ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait]
[-l preload] [-M mask | time] [-m ttl] [-p pattern] [-S src_addr]
[-s packetsize] [-T ttl] [-t timeout] [-W waittime]
[-z tos] mcast-group

Thank you
Mon

Use gob over json encoding

json is very slow, comparing normal C /bin/ping to Go ping, there's a lot of added latency.

$ /bin/ping -c 10 localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.085 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.071 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.076 ms
64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.081 ms
64 bytes from localhost (127.0.0.1): icmp_seq=5 ttl=64 time=0.063 ms
64 bytes from localhost (127.0.0.1): icmp_seq=6 ttl=64 time=0.100 ms
64 bytes from localhost (127.0.0.1): icmp_seq=7 ttl=64 time=0.082 ms
64 bytes from localhost (127.0.0.1): icmp_seq=8 ttl=64 time=0.050 ms
64 bytes from localhost (127.0.0.1): icmp_seq=9 ttl=64 time=0.101 ms
64 bytes from localhost (127.0.0.1): icmp_seq=10 ttl=64 time=0.102 ms
$ ./ping --privileged -c 10 localhost
PING localhost (127.0.0.1):
62 bytes from 127.0.0.1: icmp_seq=0 time=638.779µs
62 bytes from 127.0.0.1: icmp_seq=1 time=188.839µs
62 bytes from 127.0.0.1: icmp_seq=2 time=148.038µs
62 bytes from 127.0.0.1: icmp_seq=3 time=473.847µs
62 bytes from 127.0.0.1: icmp_seq=4 time=336.945µs
62 bytes from 127.0.0.1: icmp_seq=5 time=344.765µs
62 bytes from 127.0.0.1: icmp_seq=6 time=270.598µs
62 bytes from 127.0.0.1: icmp_seq=7 time=96.763µs
62 bytes from 127.0.0.1: icmp_seq=8 time=415.721µs
62 bytes from 127.0.0.1: icmp_seq=9 time=195.21µs

Library should return errors and not printf messages

It's probably just my opinion, but I think having Printf's when an error occurs is not the best option. There is no easy way to divert the error messages to something else, so you practically have no choice but to have the error messages end up at stdout.
May I suggest adding a feature to divert the output (to a different stream, to a logger, ....) or adding a function that disables the messages and just return an error?

Implement a per-packet timeout (i.e. -W in UNIX ping)

335          fmt.Println("get package: ",p.PacketsRecv)
336		if p.Count > 0 && p.PacketsRecv >= p.Count {
			close(p.done)
			wg.Wait()
			return
		}

change p.PacketsRecv to p.PacketsSent it work , but it impossible to collect the last package after using p.PacketsSent :(

24 bytes from 52.34.112.193: icmp_seq=0 time=186.8937ms
get package:  1
get package:  1
get package:  1
24 bytes from 52.34.112.193: icmp_seq=2 time=188.8935ms
get package:  2
get package:  2
get package:  2
24 bytes from 52.34.112.193: icmp_seq=4 time=189.8887ms
get package:  3
get package:  3
24 bytes from 52.34.112.193: icmp_seq=5 time=188.89ms
get package:  4
get package:  4
24 bytes from 52.34.112.193: icmp_seq=6 time=188.8929ms
get package:  5
get package:  5
24 bytes from 52.34.112.193: icmp_seq=7 time=188.8929ms
get package:  6
get package:  6
24 bytes from 52.34.112.193: icmp_seq=8 time=195.8847ms
get package:  7
get package:  7
get package:  7
get package:  7
get package:  7
get package:  7
get package:  7
get package:  7
get package:  7

slice bounds out of range

Hi!

I have the same issue, as in #47

Here is my simple code:

package main

import (
    "github.com/sparrc/go-ping"
)

func main() {
    pinger, err := ping.NewPinger("google.com")
    if err != nil {
            panic(err)
    }
    pinger.SetPrivileged(true)
    pinger.Count = 3
    pinger.Run()
}

And the error:

panic: runtime error: slice bounds out of range

goroutine 1 [running]:
github.com/sparrc/go-ping.(*Pinger).processPacket(0xc0000929c0, 0xc0000fa090, 0x4, 0x3)
	/home/g0rbe/go/src/github.com/sparrc/go-ping/ping.go:460 +0x828
github.com/sparrc/go-ping.(*Pinger).run(0xc0000929c0)
	/home/g0rbe/go/src/github.com/sparrc/go-ping/ping.go:331 +0x500
github.com/sparrc/go-ping.(*Pinger).Run(...)
	/home/g0rbe/go/src/github.com/sparrc/go-ping/ping.go:278
main.main()

Your sample (cmd/ping/ping) also panic.

(Fresh download, executed as root, running on Arch linux)

Timeout issue

Hi i created the following go function.
When i do not use a timeout, it works. When i set a timeout it says one packet send 0 recieved.
Then it stops.
Perry

// Test internet connection by sending 3 pings to www.google.com.
func testInternet(hostname string) (ok bool) {

pinger, err := ping.NewPinger(hostname)
if err != nil {
	fmt.Printf("ERROR: %s\n", err.Error())
	return
}

pinger.OnRecv = func(pkt *ping.Packet) {
	fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n", pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
}

pinger.OnFinish = func(stats *ping.Statistics) {
	fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr)
	fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n", stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
	fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n", stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
}

fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())

pinger.Count = 3
pinger.Timeout =  5 * 100000
pinger.SetPrivileged(true)
pinger.Run()

fmt.Printf("Package Send : %d, Package Recieved %d\n", pinger.PacketsSent, pinger.PacketsRecv)

return (pinger.PacketsRecv > 0)

}

Pinger.Stop() is unsafe when used with a timeout or count

Calling the Stop() method on a pinger with a timeout or count configured can cause a panic, if the Stop() is called after the Pinger has exceeded either the timeout or ping count:

panic: close of closed channel

goroutine 10 [running]:
github.com/sparrc/go-ping.(*Pinger).Stop(...)
	/go/pkg/mod/github.com/sparrc/[email protected]/ping.go:345

There's no way to safely prevent this panic externally without some kind of race condition.

CPU utilization problem

There is a problem with CPU utilization on the for/select block on line 293 of ping.go. Essentially when you have a large number of pingers running the "default" case causes the for loop to spin up the CPU. I had 700% CPU utilization on my mac.

The fix for this seems to be to move the default case outside of the select block. I haven't tested all the permutations here.

	for {
		select {
		case <-c:
			close(p.done)
		case <-p.done:
			wg.Wait()
			return
		case <-timeout.C:
			close(p.done)
			wg.Wait()
			return
		case <-interval.C:
			err = p.sendICMP(conn)
			if err != nil {
				fmt.Println("FATAL: ", err.Error())
			}
		case r := <-recv:
			err := p.processPacket(r)
			if err != nil {
				fmt.Println("FATAL: ", err.Error())
			}
		}
                // moved outside of the select block
		if p.Count > 0 && p.PacketsRecv >= p.Count {
			close(p.done)
			wg.Wait()
			return
		}
	}

Duplicate fields in Pinger struct

This merge request appears to have created some duplicate fields: #15

duplicate field name in struct literal: network
duplicate field name in struct literal: ipv4

memory leak ?

i wrote a ping service program, it's will ping a lot of servers every period.
i found the memory of program increased unlimited.

init ping.NewPinger when ping a ip

please help ?
ping_agent
ping_agent2

how get the ttl in ping

how can i get the ttl in ping result?
i tried to get it in ipv4Payload but the result is bad

type IPHeader struct {
	version_ihl    byte
	dscp_ecn       byte
	total_length   uint16
	identification uint16
	offset         uint16
	ttl            byte
	protocol       byte
	checksum       uint16
	source_address uint32
	dest_address   uint32
}

func ipv4Payload(b []byte) []byte {
	if len(b) < ipv4.HeaderLen {
		return b
	}
	hdrlen := int(b[0]&0x0f) << 2
	fmt.Println(unsafe.Pointer(&b[hdrlen:][0]))
	fmt.Println((*IPHeader)(unsafe.Pointer(&b[hdrlen:][6])))
	return b[hdrlen:]
}

Multiple ping got mixed

I don't see any id/seq number on ping sent, running multiples ping from same host will mix results.

not implemented on windows/amd64

version: github.com/go-ping/ping v0.0.0-20200918120429-e8ae07c3cec8

Run following code on Windows 10, it says not implemented on windows/amd64.

package main

import (
	"fmt"

	"github.com/go-ping/ping"
)

func main() {
	pinger, err := ping.NewPinger("180.76.76.76")
	pinger.SetPrivileged(true)
	pinger.Count = 3
	err = pinger.Run()
	if err != nil {
		fmt.Println(err)
	}
}

painc runtime error: slice bounds out of range

slice bounds out of range happen when i run ipv4 , i guess the root cause is ipv4Payload method

func (p *Pinger) processPacket(recv *packet) error {
	var bytes []byte
	var proto int
	if p.ipv4 {
		if p.network == "ip" {
			bytes = ipv4Payload(recv)   // this method should change recv.nbytes when process ipv4Header
		} else {
			bytes = recv.bytes
		}
		proto = protocolICMP
	} else {
		bytes = recv.bytes
		proto = protocolIPv6ICMP
	}

	var m *icmp.Message
	var err error
       // if no change recv.nbytes, the bytes will slice bounds out of range
	if m, err = icmp.ParseMessage(proto, bytes[:recv.nbytes]); err != nil {
		return fmt.Errorf("Error parsing icmp message")
	}
}

func ipv4Payload(recv *packet) []byte {
	b := recv.bytes
	if len(b) < ipv4.HeaderLen {
		return b
	}
	hdrlen := int(b[0]&0x0f) << 2
	recv.nbytes -= hdrlen  // this is need
	return b[hdrlen:]
}

Like a memory leak

Like a memory leak
I keep looping around, and memory keeps increasing
like:
for {
pinger, err := ping.NewPinger("192.168.80.2")
pinger.SetPrivileged(true)
if err != nil {
panic(err)
}
pinger.Count = 2
pinger.Timeout = 10 * time.Millisecond
pinger.Run() // blocks until finished
stats := pinger.Statistics()
fmt.Println(len(stats.Rtts) == 0)
time.Sleep(time.Millisecond * 1)
}

Multiple ICMP requests get muddled when running concurrently

Problem:
Non pingable IP addresses are receiving ping results from other ping requests when run in parallel.

Example Code:

package main

import (
	"fmt"
	"sync"

	"github.com/sparrc/go-ping"
)

// PingCheck --
func PingCheck(HostToPing string) ping.Statistics {
	pinger, err := ping.NewPinger(HostToPing)
	if err != nil {
		panic(err)
	}

	pinger.Count = 1
	pinger.Timeout = 1000000000

	pinger.OnRecv = func(pkt *ping.Packet) {
		fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n",
			pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
	}

	pinger.OnFinish = func(stats ping.Statistics) {
		fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr)
		fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n",
			stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
		fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
			stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
	}

	fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())
	pinger.Run()                 // blocks until finished
	stats := pinger.Statistics() // get send/receive/rtt stats
	return stats
}

func main() {

	hosts := [...]string{"8.8.8.8", "1.2.3.4", "4.4.4.4"}

	sliceLength := len(hosts)
	var wg sync.WaitGroup
	wg.Add(sliceLength)
	for i := 0; i < sliceLength; i++ {
		go func(i int) {
			defer wg.Done()
			host := hosts[i]
			stats := PingCheck(host)
			fmt.Println(host, ": ", stats)
		}(i)
	}
	wg.Wait()
}

// 8.8.8.8 & 4.4.4.4 should work
// 1.2.3.4 should not but occasionally it returns with stats. This ip does not exist on the internet!

Output:

austinb-3GLM:agent austinb$ while true; do ./this && sleep 1; done | grep 1.2.3.4
PING 1.2.3.4 (1.2.3.4):
62 bytes from 1.2.3.4: icmp_seq=0 time=16.122ms
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {1 1 0 1.2.3.4 1.2.3.4 [16.122ms] 16.122ms 16.122ms 16.122ms 0s}
PING 1.2.3.4 (1.2.3.4):
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {0 1 100 1.2.3.4 1.2.3.4 [] 0s 0s 0s 0s}
PING 1.2.3.4 (1.2.3.4):
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {0 1 100 1.2.3.4 1.2.3.4 [] 0s 0s 0s 0s}
PING 1.2.3.4 (1.2.3.4):
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {0 1 100 1.2.3.4 1.2.3.4 [] 0s 0s 0s 0s}
PING 1.2.3.4 (1.2.3.4):
62 bytes from 1.2.3.4: icmp_seq=0 time=14.365ms
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {1 1 0 1.2.3.4 1.2.3.4 [14.365ms] 14.365ms 14.365ms 14.365ms 0s}
PING 1.2.3.4 (1.2.3.4):
62 bytes from 1.2.3.4: icmp_seq=0 time=14.198ms
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {1 1 0 1.2.3.4 1.2.3.4 [14.198ms] 14.198ms 14.198ms 14.198ms 0s}
PING 1.2.3.4 (1.2.3.4):
62 bytes from 1.2.3.4: icmp_seq=0 time=15.194ms
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {1 1 0 1.2.3.4 1.2.3.4 [15.194ms] 15.194ms 15.194ms 15.194ms 0s}
PING 1.2.3.4 (1.2.3.4):
--- 1.2.3.4 ping statistics ---
1.2.3.4 :  {0 1 100 1.2.3.4 1.2.3.4 [] 0s 0s 0s 0s}

memory leak while running for some time

I tried basic Pinger as follows:

for {
	pinger, err := ping.NewPinger("8.8.8.8")

	pinger.SetPrivileged(true)
	if err != nil {
		panic(err)
	}
	pinger.Count = 3
	pinger.Interval = time.Second
	pinger.Timeout = 3 * time.Second
	pinger.SetPrivileged(true)
	pinger.Run() // blocks until finished
	stats := pinger.Statistics()
	log.Println("%v", len(stats.Rtts) == 0)
	log.Println("%v", (stats))
}

Where start ping at t = t0 and pinger run shall be blocked for 3 secs and returns the status.

I start new pinger again for getting continuous ping statistics.

I tried finally differently by registering the callback and start the New Pinger on receiving the callback as follows:

func main() {
	pinger, err := ping.NewPinger("8.8.8.8")
	if err != nil {
		fmt.Println("Pinger Create failed: %s", err.Error())
		return err
	}

	pinger.Count = 3
	pinger.Interval = time.Second
	pinger.Timeout = 3 * time.Second
	pinger.SetPrivileged(true)
	pinger.OnFinish = onFinishCb

	err = pinger.Run()
	if err != nil {
		fmt.Println("Pinger Run failed: %s", err.Error())
		return err
	}

	wg := &sync.WaitGroup{}
	wg.Add(1)
	go shutdownHandler(wg)

	wg.Wait()
}


// onFinishCb is called when ping is finished.
func onFinishCb(stats *ping.Statistics) {
	t.latency = uint64(stats.AvgRtt / time.Millisecond)
	pinger, err := ping.NewPinger(t.peerIP)
	if err != nil {
		fmt.Println("Pinger Create failed: %s", err.Error())
		// return err
	}
	pinger.Count = 3
	pinger.Interval = time.Second
	pinger.Timeout = 3 * time.Second
	pinger.SetPrivileged(true)
	pinger.OnFinish = onFinishCb
	fmt.Println("Latency :%v", t.latency)

	err = pinger.Run()
	if err != nil {
		fmt.Println("Pinger Run failed: %s", err.Error())
	}
}

In either case I am finding memory of this main process increases from 1 MB to 8 MB in 4-5 mins (with top command).

Please provide your inputs.

pinger.OnRecv no timeout packet

Hello,

maybe i am getting this wrong. I want to print out the packet rtt which works fine with the onrecv function but i also want to print out when the ping times out.
How would i get the information if there was a timeout ?

Best regards

Can I start mutiple Pingers meantime?

    for i := range iplist {
        pingers[iplist[i]], _ = ping.NewPinger(iplist[i])
    }

    timer := time.NewTimer(time.Second * 10)
    wg := sync.WaitGroup{}

    for _, v := range pingers {
        wg.Add(1)
        go func() {
            if v != nil {
                v.Run()
            }
            wg.Done()
        }()
    }

loop:
    for {
        select {
        case <-timer.C:
            for _, v := range pingers {
                if v != nil {
                    v.Stop()
                }
            }
            break loop
        }
    }

    wg.Wait()

    for _, v := range pingers {
        fmt.Println(v.Statistics())
        if true { // v.Statistics().PacketLoss >= 0.0 {
            warningList = append(warningList, v.Statistics())
        }
    }

I run some code like this, which ping two ip address and hope they can stop after 10 seconds, but it just print as follows:

&{0 0 NaN 11.238.144.242 11.238.144.242 [] 0s 0s 0s 0s}
&{20 20 0 11.238.144.86 11.238.144.86 [62.531µs 115.415µs 66.564µs 46.571µs 55.519µs 41.02µs 52.507µs 36.631µs 47.61µs 48.643µs 130.216µs 53.706µs 67.566µs 43.757µs 49.207µs 43.15µs 41.336µs 96.105µs 58.361µs 47.551µs] 36.631µs 130.216µs 60.198µs 24.562µs}

It just seems like there is one Pinger didn't work.
So what I want to ask is, can I start mutilple Pingers like this? Or there is some mistake in my test code?

"Error listening for ICMP packets: socket: permission denied" while using this library in unprivileged ping

This library , when I am using for unprivileged ping throws "Error listening for ICMP packets: socket: permission denied" error with go version go1.7.4 .
what I am doing is-

func (p *Ping) doPing() (latency, jitter, packetLoss float64, err error) {

    timeout := time.Second*1000
    interval := time.Second
    count := 5
    host := p.ipAddr
    pinger, cmdErr := ping.NewPinger(host)
    if cmdErr != nil {
            glog.Error("Failed to ping " + p.ipAddr)
            err = cmdErr
            return
    }

    pinger.OnRecv = func(pkt *ping.Packet) {
            fmt.Println("receiving ICMP packets")
    }
    pinger.OnFinish = func(stats *ping.Statistics) {
            fmt.Println("--- %s ping statistics ---", stats.Addr)
            fmt.Println("%d pakets transmitted, %d packets received, %v%% packet loss",
                    stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
            fmt.Println("round-trip - min %d ,avg %d ,max %d ,stddev %d",
                    stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
    }

    pinger.Count = count
    pinger.Interval = interval
    pinger.Timeout = timeout
    pinger.SetPrivileged(false)
    pinger.Run()
    stats := pinger.Statistics()
    latency = float64(stats.AvgRtt)  
    jitter = float64(stats.StdDevRtt)
    packetLoss = stats.PacketLoss
    return

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.