* Protect stats with RWMutex
updateStats() now also updates rtts so it is protected by the
lock.
Statistics() should now be callable from other goroutines.
PacketsSent and PacketsRecvDuplicates can still be updated while
Statistics() is running, and thus could yield strange results
(sent<recv)
To fix this, atomics could be used, but it would require changing the
types of the fields from int to int64, thus changing the public api.
Signed-off-by: Jean Raby <jean@raby.sh>
* Fix unprivileged packet matching on Linux
* Compute statistics on-the-fly as packets are received
Signed-off-by: Jean Raby <jean@raby.sh>
Signed-off-by: Charlie Jonas <charlie@charliejonas.co.uk>
Co-authored-by: Charlie Jonas <charlie@charliejonas.co.uk>
Duplicate ICMP packets are now detected and processed using a separate callback and field in the statistics struct.
Co-authored-by: Charlie Jonas <charlie@charliejonas.co.uk>
Co-authored-by: Ben Kochie <superq@gmail.com>
This commit adds an OnSend handler, similar to the OnReceive handler but called when a packet is sent. It reuses the Packet type, but does not set the TTL or RTT.
The x/net/ipv4 and x/net/ipv6 packages do not implement the necessary
control message functionality on Microsoft Windows. This commit ignores
any control message errors that are returned by the SetControlMessage
function if the Go runtime is compiled for Windows. This fixes#105.
Signed-off-by: Charlie Jonas <charlie@charliejonas.co.uk>
Deadlock observed when starting multiple ping routines.
It happens when writting to `recv` channel (which is full) and no active
readers for this channel.
*Example to reproduce the issue* :
var waiter sync.WaitGroup
pingFunc := func() {
defer waiter.Done()
pinger, err := ping.NewPinger("8.8.8.8")
if err != nil {
return
}
pinger.SetPrivileged(true)
pinger.Count = 5
pinger.Interval = time.Second
pinger.Timeout = time.Second * 4
pinger.Run()
}
for i := 0; i < 1000; i++ {
waiter.Add(1)
go pingFunc()
}
waiter.Wait() // deadlock here! (reproducible almost every time)
Move the DNS resolver out of the NewPinger() function in order to allow
adjusting of IPv4 vs IPv6 DNS resolution before running. This also
allows the user to verify resolution.
* Create new `New()` method that returns a bare default struct.
* Create new `Resolve()` method.
* Call `Resolve()` from `SetAddr()`.
* Call `Resolve()` automatically from `Run()`.
* Remove unecessary private `run()` method.
Update ping command for simplifed return values of `NewPinger()`.
Signed-off-by: Ben Kochie <superq@gmail.com>
Commit d046b245 introduces a bug which causes ping to always fail.
The source of this bug is:
```
// Check if reply from same ID
body := m.Body.(*icmp.Echo)
if body.ID != p.id {
return nil
}
```
Which due to the selection of p.id requires that SetPrivileged is
set to true. In the case where Privileged (i.e p.network == udp)
it is left to the kernel to set the ICMP id.
https://lwn.net/Articles/443051/ Discusses the introduction of
non-setuid-less ping. The kernel implementation for this
interface dictates using the local port, which gets mapped into
the ping_table struct. There is no current implementation in the
go icmp library to address this problem directly.
To address this issue, I've added a `Tracker` field for `Pinger`
as well as `IcmpData` datastructure to allow for uniquely tracking
icmp requests. The id (as with the `id` field) is not unique,
but will statistically rare for duplicates.