Documentation
¶
Overview ¶
Package icmpengine sends non-privileged ICMP echo requests and receives the replies concurrently, without blocking on per-packet timeouts. It is designed to be embedded in other Go programs.
Non-privileged ICMP uses IPPROTO_ICMP sockets (see https://lwn.net/Articles/422330/). On Linux this requires the ping group range sysctl to include the running user's gid, e.g.:
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
Typical use:
eng, err := icmpengine.New(icmpengine.WithLogger(logger))
if err != nil { ... }
if err := eng.Start(ctx); err != nil { ... }
defer eng.Close()
res, err := eng.Ping(ctx, netip.MustParseAddr("8.8.8.8"), 10, 100*time.Millisecond)
Index ¶
- Constants
- Variables
- type Backend
- type Engine
- func (e *Engine) Close() error
- func (e *Engine) Err() error
- func (e *Engine) Ping(ctx context.Context, addr netip.Addr, count int, interval time.Duration, ...) (Result, error)
- func (e *Engine) PingAll(ctx context.Context, concurrency int, targets []Target) ([]Result, error)
- func (e *Engine) Start(ctx context.Context) error
- type ICMPEchoReply
- type Option
- func WithDSCP(v int) Option
- func WithDontFragment(b bool) Option
- func WithExpiryBackend(b Backend) Option
- func WithFakeSuccess(b bool) Option
- func WithHackSysctl(b bool) Option
- func WithLogger(l *slog.Logger) Option
- func WithReadDeadline(d time.Duration) Option
- func WithReceivers(v4, v6 int) Option
- func WithSource(addr netip.Addr) Option
- func WithSplayReceivers(b bool) Option
- func WithTTL(n int) Option
- func WithTimeout(d time.Duration) Option
- type PingOption
- type Result
- type Target
Constants ¶
const IsRaceEnabled = false
IsRaceEnabled reports if the race detector is enabled.
Variables ¶
var ( // ErrNotStarted is returned by Ping when the engine has not been Started // (or has already been Closed). ErrNotStarted = errors.New("icmpengine: engine not started") // ErrAlreadyStarted is returned by Start when called more than once. ErrAlreadyStarted = errors.New("icmpengine: engine already started") // ErrCountRange is returned by Ping when count is outside [0, 65535] // (ICMP sequence numbers are 16 bits). ErrCountRange = errors.New("icmpengine: count out of range [0,65535]") // ErrDuplicatePing is returned by Ping when another Ping to the same // address is already in flight on this engine. ErrDuplicatePing = errors.New("icmpengine: address already being pinged") // ErrInvalidAddr is returned by Ping when the destination address is not // a valid IPv4 or IPv6 address. ErrInvalidAddr = errors.New("icmpengine: invalid destination address") // ErrTimeoutRange is returned by Ping when a PingTimeout value is negative. ErrTimeoutRange = errors.New("icmpengine: ping timeout must be >= 0") // ErrPayloadSizeRange is returned by Ping when a PayloadSize value is // outside [0, maxPayloadSize]. ErrPayloadSizeRange = errors.New("icmpengine: payload size out of range [0,65500]") // ErrDSCPRange is returned by Ping when a DSCP value is outside [0, 63]. ErrDSCPRange = errors.New("icmpengine: dscp out of range [0,63]") // ErrTTLRange is returned by New when a WithTTL value is outside [0, 255] // (0 means keep the kernel default). ErrTTLRange = errors.New("icmpengine: ttl out of range [0,255]") // ErrDontFragmentUnsupported is returned by Start when WithDontFragment was // set on a platform where the engine cannot set it on non-privileged sockets // (everything except Linux). ErrDontFragmentUnsupported = errors.New("icmpengine: dont fragment is only supported on linux") )
Sentinel errors returned by the engine.
Functions ¶
This section is empty.
Types ¶
type Backend ¶
type Backend int
Backend selects the expiry-tracking data structure used by an Engine.
const ( // BackendHeap uses container/heap, a binary min-heap. Peek is O(1); push and // remove are O(log n). No external dependency. BackendHeap Backend = iota // BackendBTree uses github.com/google/btree. All operations are O(log n). BackendBTree // BackendDaryHeap uses an 8-ary array heap — shallower than the binary heap, // which benchmarks fastest on this remove-heavy workload (see BenchmarkDaryFanout). // It is the default: New with no WithExpiryBackend uses it. BackendDaryHeap // BackendRadix uses a fixed-base MSD radix trie over the 64-bit expiry key; // all operations are O(64/8)=O(8), independent of n. BackendRadix // BackendPairing uses a pairing heap (O(1) push/peek). BackendPairing // BackendTimingWheel uses a hierarchical absolute-time timing wheel with a // btree overflow. Best under near-monotonic load; see expiry_wheel.go. BackendTimingWheel )
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine sends ICMP echo requests and matches them to replies. Create one with New, Start it, call Ping/PingAll, then Close it. An Engine is safe for concurrent use by multiple goroutines. It implements io.Closer.
func New ¶
New creates an Engine from the given options. It validates configuration and returns an error instead of terminating the process. New does not open sockets or start goroutines; call Start for that.
func (*Engine) Close ¶
Close shuts the engine down: it cancels the engine context, waits for all in-flight pings and background goroutines to finish, and closes the sockets. Close is idempotent and returns the first fatal background error, if any.
func (*Engine) Err ¶
Err returns the first fatal error observed by a background (receiver) goroutine, or nil. A non-nil Err means the engine has begun shutting itself down.
func (*Engine) Ping ¶
func (e *Engine) Ping(ctx context.Context, addr netip.Addr, count int, interval time.Duration, opts ...PingOption) (Result, error)
Ping sends count echo requests to addr, interval apart, and returns aggregated statistics. It blocks until every packet has been answered or timed out, or until ctx (or the engine) is canceled. On cancellation it returns the partial Result gathered so far alongside a non-nil error.
type ICMPEchoReply ¶
ICMPEchoReply represents Echo Reply messages per the IPv4/rfc792 and IPv6/rfc2463 ( see extended comments below )
func ParseICMPEchoReply ¶
func ParseICMPEchoReply(b []byte) (*ICMPEchoReply, error)
ParseICMPEchoReply parses the ICMP echo reply messages This was originally based on on the the golang standard icmp ParseMessage, which for unknown reasons don't parse ICMP echo https://pkg.go.dev/golang.org/x/net/icmp#ParseMessage https://github.com/golang/net/blob/7fd8e65b6420/icmp/message.go#L139
func ParseICMPEchoReplyBB ¶
func ParseICMPEchoReplyBB(b bytes.Buffer) (*ICMPEchoReply, error)
ParseICMPEchoReplyBB is the same as ParseICMPEchoReply, except uses bytes.Buffer, instead of []byte This is mostly to allow use of sync.Pool, which should be faster (maybe?) https://www.akshaydeo.com/blog/2017/12/23/How-did-I-improve-latency-by-700-percent-using-syncPool/
type Option ¶
type Option func(*config)
Option configures an Engine created by New.
func WithDSCP ¶
WithDSCP marks every echo request sent by this engine with the given 6-bit DiffServ code point (0-63). It is written into the IPv4 ToS / IPv6 Traffic Class byte as v<<2 (the low two ECN bits stay zero) and applied once, at Start, to both the IPv4 and IPv6 socket. It is engine-wide rather than per-ping because the engine shares one socket per family across all concurrent pings. Defaults to 0 (unmarked). Values outside [0, 63] are rejected by New.
func WithDontFragment ¶ added in v1.2.0
WithDontFragment sets the IPv4 Don't-Fragment bit (and the IPv6 equivalent) on outgoing echo requests by enabling path-MTU discovery on the socket (IP_MTU_DISCOVER / IPV6_MTU_DISCOVER = DO). Combined with WithPayloadSize it lets a caller probe the path MTU without privileged raw sockets. It is applied at Start; on platforms other than Linux, Start returns ErrDontFragmentUnsupported. Defaults to false.
func WithExpiryBackend ¶
WithExpiryBackend selects the data structure used to track outstanding pings. Defaults to BackendDaryHeap (the fastest in benchmarks); see docs/backends.md.
func WithFakeSuccess ¶
WithFakeSuccess makes the engine synthesize successful replies without opening sockets or sending packets. Intended for testing only.
func WithHackSysctl ¶
WithHackSysctl allows the engine, when running as root, to run "sysctl -w net.ipv4.ping_group_range=0 2147483647" if opening sockets fails. Off by default; opt in only if you understand the implication.
func WithLogger ¶
WithLogger sets the structured logger. A nil logger (the default) discards all log output.
func WithReadDeadline ¶
WithReadDeadline sets the receiver socket read deadline. It bounds how quickly receivers notice Close/cancellation; it is not a per-ping timeout. Defaults to 1s.
func WithReceivers ¶
WithReceivers sets the number of receiver goroutines per protocol. Defaults to 2 and 2.
func WithSource ¶ added in v1.2.0
WithSource binds the engine's sockets to a specific source address, so echo requests leave from that address (useful on multi-homed hosts). The address applies to its own family only: an IPv4 source binds the IPv4 socket and leaves the IPv6 socket on its default (and vice versa). A zero Addr (the default) binds to the wildcard address. Binding is done at Start; an address that is not a local address makes Start fail.
func WithSplayReceivers ¶
WithSplayReceivers staggers receiver startup over the read deadline instead of starting them all at once. Defaults to false.
func WithTTL ¶
WithTTL sets the outgoing IPv4 TTL (and the equivalent IPv6 hop limit) for every echo request sent by this engine. It is applied once, at Start, to both sockets — engine-wide rather than per-ping for the same reason as WithDSCP. Defaults to 64 (the Linux ping / kernel default); a value of 0 keeps the kernel default without setting the option. Values outside [0, 255] are rejected by New.
func WithTimeout ¶
WithTimeout sets how long to wait for an echo reply before a ping is counted as a failure. Defaults to 1s.
type PingOption ¶
type PingOption func(*pingConfig)
PingOption customizes a single Ping call.
func DropProbability ¶
func DropProbability(p float64) PingOption
DropProbability fakes packet loss with the given probability in [0,1] by not actually sending the echo request. Intended for testing only.
func PayloadSize ¶
func PayloadSize(n int) PingOption
PayloadSize sets the number of ICMP data bytes appended after the 8-byte ICMP header, like ping's -s option (so PayloadSize(56) yields a 64-byte ICMP message). The kernel adds the IP header on top. A value of 0 (the default) sends a bare echo request. Values outside [0, 65500] are rejected by Ping.
func PingTimeout ¶
func PingTimeout(d time.Duration) PingOption
PingTimeout overrides the engine's default timeout for this Ping call, so different destinations can wait different amounts of time before a packet is counted as a failure (e.g. 10ms on a LAN, hours for an interplanetary link). A zero value keeps the engine default; a negative value is rejected.
type Result ¶
type Result struct {
IP netip.Addr
Successes int
Failures int
OutOfOrder int
RTTs []time.Duration
Count int
Min time.Duration
Max time.Duration
Mean time.Duration
Variance time.Duration
Sum time.Duration
// Duration is the wall-clock time the Ping call took.
Duration time.Duration
}
Result holds the aggregated statistics for a single Ping call.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
icmpengine
command
|
|
|
example
|
|
|
concurrent
command
Command concurrent pings many hosts in parallel with a bounded worker pool and prints a per-host summary.
|
Command concurrent pings many hosts in parallel with a bounded worker pool and prints a per-host summary. |
|
simple
command
Command simple pings a single host and prints the round-trip statistics.
|
Command simple pings a single host and prints the round-trip statistics. |