ping

package module
v1.4.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 27, 2022 License: MIT Imports: 16 Imported by: 0

README

pingo Go Reference Go

Fast and lightweight ping library for Golang with multi-host support.

Features

  • ICMP sockets:
    • UDP port 0 means "let the kernel pick a free number"
    • ICMP Echo Message ID is UDP port, so multiple instances of Pinger do not collide
  • Support for custom setsockopt(2) and sendmsg(2) options
  • Support for Linux kernel RX and TX timestamps with SO_TIMESTAMPING
  • IPv4 and IPv6 support
  • ICMP sequence numbers manager (no random): O(1) time, 256kB of memory
  • Context awareness

Requirements

  • go >= 1.16
  • Linux kernel >= 3.11

You may need to adjust ping_group_range sysfs to allow the creation of ICMP sockets:

$ echo 0 2147483647 > /proc/sys/net/ipv4/ping_group_range

Example

Here is a simple traceroute(8) implementation:

dst := net.IPv4(8, 8, 8, 8)

p, err := New(nil)
if err != nil {
	fmt.Println(err)
	return
}
defer p.Close()

ctx, cancel := context.WithCancel(context.Background())
var g errgroup.Group
g.Go(func() error {
	return p.Listen(ctx)
})
defer func() {
	cancel()
	if err := g.Wait(); !errors.Is(err, context.Canceled) {
		fmt.Println(err)
	}
}()

for ttl := uint8(1); ttl < math.MaxUint8-1; ttl++ {
	fmt.Printf("%3d: ", ttl)
	r, err := p.PingContextTimeout(ctx, dst, 1*time.Second, TTL(ttl))
	if errors.Is(err, context.DeadlineExceeded) {
		// no answer from current hop
		fmt.Println("...")
		continue
	}
	from := dst
	switch err := err.(type) {
	case nil:
	case TimeExceededError:
		from = err.From()
	default:
		fmt.Println(err)
		return
	}
	fmt.Printf("%-15s %s\n", from, r.RTT)
	if err == nil {
		return
	}
}
fmt.Println("TTL maxed out")

Documentation

Overview

Example (Traceroute)
dst := net.IPv4(8, 8, 8, 8)

p, err := New(nil)
if err != nil {
	fmt.Println(err)
	return
}
defer p.Close()

ctx, cancel := context.WithCancel(context.Background())
var g errgroup.Group
g.Go(func() error {
	return p.Listen(ctx)
})
defer func() {
	cancel()
	if err := g.Wait(); !errors.Is(err, context.Canceled) {
		fmt.Println(err)
	}
}()

for ttl := uint8(1); ttl < math.MaxUint8-1; ttl++ {
	fmt.Printf("%3d: ", ttl)
	r, err := p.PingContextTimeout(ctx, dst, 1*time.Second, TTL(ttl))
	if errors.Is(err, context.DeadlineExceeded) {
		// no answer from current hop
		fmt.Println("...")
		continue
	}
	from := dst
	switch err := err.(type) {
	case nil:
	case TimeExceededError:
		from = err.From()
	default:
		fmt.Println(err)
		return
	}
	fmt.Printf("%-15s %s\n", from, r.RTT)
	if err == nil {
		return
	}
}
fmt.Println("TTL maxed out")
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func HopLimit added in v1.1.0

func HopLimit(hl uint8) unixx.IValueSockOpt[uint8, uint32]

TTL is <SOL_IPV6, IPV6_HOPLIMIT>

func MTU added in v1.1.0

func MTU(mtu int32) *unixx.ValueSockOpt[int32]

MTU is <SOL_IP, IP_MTU>

func MTU6 added in v1.1.0

func MTU6(mtu int32) *unixx.ValueSockOpt[int32]

MTU6 is <SOL_IPV6, IPV6_MTU>

func Mark added in v1.1.0

func Mark(m uint32) *unixx.ValueSockOpt[uint32]

Mark is <SOL_SOCKET, SO_MARK>

func TTL added in v1.1.0

TTL is <SOL_IP, IP_TTL>

func TrafficClass added in v1.1.0

func TrafficClass(tc uint8) unixx.IValueSockOpt[uint8, uint32]

TrafficClass is <SOL_IPV6, IPV6_TCLASS>

Types

type DestinationUnreachableError added in v1.1.0

type DestinationUnreachableError struct {
	// contains filtered or unexported fields
}

func NewDestinationUnreachableError added in v1.1.0

func NewDestinationUnreachableError(from net.IP,
	code DstUnreachableCode) DestinationUnreachableError

func (DestinationUnreachableError) Error added in v1.1.0

func (DestinationUnreachableError) From added in v1.1.0

func (DestinationUnreachableError) Unwrap added in v1.1.0

type DstUnreachableCode added in v1.1.0

type DstUnreachableCode uint8
const (
	NetUnreachable DstUnreachableCode = iota
	HostUnreachable
	ProtocolUnreachable
	PortUnreachable
	FragmentationNeeded
	SourceRouteFailed
)

func (DstUnreachableCode) Error added in v1.1.0

func (c DstUnreachableCode) Error() string

type ICMPError added in v1.1.0

type ICMPError interface {
	error
	From() net.IP
}

type Pinger

type Pinger struct {
	// contains filtered or unexported fields
}

func New

func New(laddr *net.UDPAddr, opts ...unixx.SockOpt) (p *Pinger, err error)

New creates a new Pinger with given local address to bind to. If laddr is nil or laddr.IP is nil, then it will be bound to 0.0.0.0. opts are setsockopt(2) options to set on the underlying socket.

To enable receiving packets, Listen() should be called on returned Pinger. Close() should be called after Listen() returns.

func (*Pinger) Close

func (p *Pinger) Close() error

Close releases resources allocated for Pinger. In particular, it closes the underlying socket.

func (*Pinger) Get added in v1.1.0

func (p *Pinger) Get(opts ...unixx.SockOpt) error

Get gets given options from the underlying socket with getsockopt(2)

func (*Pinger) IsIPv6 added in v1.1.3

func (p *Pinger) IsIPv6() bool

IsIPv6 returns whether IPv6 is used, otherwise IPv4

func (*Pinger) Listen

func (p *Pinger) Listen(ctx context.Context) error

Listen handles receiving of incomming replies and dispatches them into calling Pinger.Ping* method, so *no* Pinger.Ping*() methods should be called before Listen and after it returns.

NOTE: It is a blocking call, so it should be run as a separate goroutine. It returns a non-nil error if context is done or an error occured while receiving on sokcet.

func (*Pinger) Ping

func (p *Pinger) Ping(dst net.IP, opts ...unixx.SockOpt) (Reply, error)

Ping is like PingContext, but with background context.

func (*Pinger) PingCh

func (p *Pinger) PingCh(dst net.IP, opts ...unixx.SockOpt) <-chan Reply

PingCh is like PingChPayload, but with no payload.

func (*Pinger) PingChContext

func (p *Pinger) PingChContext(ctx context.Context, dst net.IP, opts ...unixx.SockOpt) <-chan Reply

PingChContext is like PingChContextPayload, but with no payload.

func (*Pinger) PingChContextInterval

func (p *Pinger) PingChContextInterval(ctx context.Context, dst net.IP,
	interval time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChContextInterval is like PingChContextPayloadInterval, but with no payload.

func (*Pinger) PingChContextIntervalTimeout

func (p *Pinger) PingChContextIntervalTimeout(ctx context.Context, dst net.IP,
	interval, timeout time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChContextIntervalTimeout is like PingChContextPayloadIntervalTimeout, but with no payload.

func (*Pinger) PingChContextPayload added in v1.1.0

func (p *Pinger) PingChContextPayload(ctx context.Context, dst net.IP,
	payload []byte, opts ...unixx.SockOpt) <-chan Reply

PingChContextPayload is the same as PingChContextPayloadTimeout, but with no timeout, so it waits for each reply until context is done.

func (*Pinger) PingChContextPayloadInterval added in v1.1.0

func (p *Pinger) PingChContextPayloadInterval(ctx context.Context, dst net.IP,
	payload []byte, interval time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChContextPayloadInterval is the same as PingChContextIntervalTimeout, but with timeout equal to the interval, so it waits for reply to each request until interval has passed.

func (*Pinger) PingChContextPayloadIntervalTimeout added in v1.1.0

func (p *Pinger) PingChContextPayloadIntervalTimeout(ctx context.Context, payload []byte,
	dst net.IP, interval, timeout time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChContextPayloadIntervalTimeout sends ICMP Echo Requests with given payload periodically with given interval (zero interval means send next packet righ after the reply to the previuos one has been recieved) and waits for every reply until given context is done or non-zero timeout is passed. It returns a channel, where replies are sent to. The channel is closed when the context is done, so the caller should receive on that channel until it is closed.

func (*Pinger) PingChContextPayloadTimeout added in v1.1.0

func (p *Pinger) PingChContextPayloadTimeout(ctx context.Context, dst net.IP,
	payload []byte, timeout time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChContextPayloadTimeout is the same as PingChContextPayloadIntervalTimeout, but echo requests are sent one by one, without waiting for interval to pass.

func (*Pinger) PingChContextTimeout

func (p *Pinger) PingChContextTimeout(ctx context.Context, dst net.IP,
	timeout time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChContextTimeout is like PingChContextPayloadTimeout, but with no payload.

func (*Pinger) PingChInterval

func (p *Pinger) PingChInterval(dst net.IP, interval time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChInterval is like PingChPayloadInterval, but with no payload.

func (*Pinger) PingChPayload added in v1.1.0

func (p *Pinger) PingChPayload(dst net.IP, payload []byte, opts ...unixx.SockOpt) <-chan Reply

PingChPayload is the same as PingChContextPayload, but with background context, so it pings forever.

func (*Pinger) PingChPayloadInterval added in v1.1.0

func (p *Pinger) PingChPayloadInterval(dst net.IP, payload []byte,
	interval time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChPayloadInterval is the same as PingChContextPayloadInterval, but with background timeout, so it pings forever.

func (*Pinger) PingChPayloadTimeout added in v1.1.0

func (p *Pinger) PingChPayloadTimeout(dst net.IP, payload []byte,
	timeout time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChPayloadTimeout is the same as PingChContextPayloadTimeout, but with background context, so it pings forever.

func (*Pinger) PingChTimeout

func (p *Pinger) PingChTimeout(dst net.IP, timeout time.Duration, opts ...unixx.SockOpt) <-chan Reply

PingChTimeout is like PingChPayloadTimeout, but with no payload

func (*Pinger) PingContext

func (p *Pinger) PingContext(ctx context.Context, dst net.IP, opts ...unixx.SockOpt) (Reply, error)

PingContext is like PingContextPayload, but with no payload.

func (*Pinger) PingContextPayload added in v1.1.0

func (p *Pinger) PingContextPayload(ctx context.Context, dst net.IP, payload []byte,
	opts ...unixx.SockOpt) (Reply, error)

PingContextPayload sends one ICMP Echo Request to given destination with given payload and waits for the reply until the given context is done. opts can be used to set per-packet sendmsg(2) options

On success, it returns the reply. Otherwise, it returns an error occured while sending on underlying socket, ctx.Err() or ICMPError. If the returned error is ICMPError, then the returned Reply contains valid fields and has the same Err.

Example
p, err := New(nil)
if err != nil {
	fmt.Println(err)
	return
}
defer p.Close()

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
var g errgroup.Group
g.Go(func() error {
	return p.Listen(ctx)
})
defer func() {
	cancel()
	if err := g.Wait(); !errors.Is(err, context.DeadlineExceeded) &&
		!errors.Is(err, context.Canceled) {
		fmt.Println(err)
	}
}()

payload := "HELLO, ARE YOU THERE?"
r, err := p.PingContextPayload(ctx, net.IPv4(8, 8, 8, 8), []byte(payload))
switch err.(type) {
case nil:
	fmt.Printf("RTT: %s, TTL: %d, payload: %s\n", r.RTT, r.TTL, string(r.Data))
case ICMPError:
	fmt.Printf("RTT: %s, TTL: %d, ICMP error: %s\n", r.RTT, r.TTL, err)
default:
	fmt.Println(err)
}
Output:

func (*Pinger) PingContextPayloadTimeout added in v1.1.0

func (p *Pinger) PingContextPayloadTimeout(ctx context.Context, dst net.IP,
	payload []byte, timeout time.Duration, opts ...unixx.SockOpt) (Reply, error)

PingContextTimeoutPayload is like PingContextPayload, but it waits for the reply until timeout is passed or given context id done. Zero timeout means no timeout, so PingContextTimeout(ctx, dst, 0) is equialent to PingContext(ctx, dst)

func (*Pinger) PingContextTimeout

func (p *Pinger) PingContextTimeout(ctx context.Context, dst net.IP,
	timeout time.Duration, opts ...unixx.SockOpt) (Reply, error)

PingContextTimeout is like PingContextPayloadTimeout, but with no payload.

func (*Pinger) PingN

func (p *Pinger) PingN(dst net.IP, n int, opts ...unixx.SockOpt) (Replies, error)

PingN is like PingNPayload, but with no payload

func (*Pinger) PingNContext

func (p *Pinger) PingNContext(ctx context.Context, dst net.IP, n int,
	opts ...unixx.SockOpt) (Replies, error)

PingNContext is like PingNContextPayload, but with no payload.

func (*Pinger) PingNContextInterval

func (p *Pinger) PingNContextInterval(ctx context.Context, dst net.IP, n int,
	interval time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNContextInterval is like PingNContextPayloadInterval, but with no payload.

Example
p, err := New(nil)
if err != nil {
	fmt.Println(err)
	return
}
defer p.Close()

ctx, cancel := context.WithCancel(context.Background())
var g errgroup.Group
g.Go(func() error {
	return p.Listen(ctx)
})
defer func() {
	cancel()
	if err := g.Wait(); !errors.Is(err, context.Canceled) {
		fmt.Println(err)
	}
}()

const send = 3
rs, err := p.PingNContextInterval(ctx, net.IPv4(8, 8, 8, 8), send, 1*time.Second)
if err != nil {
	fmt.Println(err)
	return
}
fmt.Printf("packet loss: %.2f%%, avg RTT: %s\n",
	100*float32(send-len(rs))/float32(send), rs.AvgRTT())
Output:

func (*Pinger) PingNContextPayload added in v1.1.0

func (p *Pinger) PingNContextPayload(ctx context.Context, dst net.IP, n int,
	payload []byte, opts ...unixx.SockOpt) (Replies, error)

PingNContextPayload is the same as PingNContextPayloadTimeout, but with no timeout, so it waits for each reply until context is done.

func (*Pinger) PingNContextPayloadInterval added in v1.1.0

func (p *Pinger) PingNContextPayloadInterval(ctx context.Context, dst net.IP, n int,
	payload []byte, interval time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNContextInterval is the same as PingNContextIntervalTimeout, but with timeout equal to the interval, so it waits for reply to each request until interval has passed.

func (*Pinger) PingNContextPayloadIntervalTimeout added in v1.1.0

func (p *Pinger) PingNContextPayloadIntervalTimeout(ctx context.Context, dst net.IP, n int,
	payload []byte, interval, timeout time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNContextPayloadIntervalTimeout sends at most n ICMP Echo Requests with a given payload and interval (zero interval means send packets one by one and do not wait for interval to pass) and returns slice of received Replies until the first occurred connection error if there was any. Zero timeout means wait for each reply until the context is done.

func (*Pinger) PingNContextPayloadTimeout added in v1.1.0

func (p *Pinger) PingNContextPayloadTimeout(ctx context.Context, dst net.IP, n int,
	payload []byte, timeout time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNContextPayloadTimeout is the same as PingNContextPayloadIntervalTimeout, but echo requests are sent one by one, without waiting for interval to pass.

func (*Pinger) PingNContextTimeout

func (p *Pinger) PingNContextTimeout(ctx context.Context, dst net.IP, n int,
	timeout time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNContextTimeout is like PingNContextPayloadTimeout, but with no payload.

func (*Pinger) PingNInterval

func (p *Pinger) PingNInterval(dst net.IP, n int, interval time.Duration,
	opts ...unixx.SockOpt) (Replies, error)

PingNInterval is like PingNPayloadInterval, but with no payload.

func (*Pinger) PingNPayload added in v1.1.0

func (p *Pinger) PingNPayload(dst net.IP, n int, payload []byte,
	opts ...unixx.SockOpt) (Replies, error)

PingNPayload is the same as PingNContextPayload, but with background context, so it tries to ping exactly n times.

func (*Pinger) PingNPayloadInterval added in v1.1.0

func (p *Pinger) PingNPayloadInterval(dst net.IP, n int, payload []byte,
	interval time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNPayloadInterval is the same as PingNPayloadTimeoutInterval, but with background context, so it tries to ping exactly n times.

func (*Pinger) PingNPayloadTimeout added in v1.1.0

func (p *Pinger) PingNPayloadTimeout(dst net.IP, n int, payload []byte,
	timeout time.Duration, opts ...unixx.SockOpt) (Replies, error)

PingNPayloadTimeout is the same as PingNContextPayloadTimeout, but with background context, so it tries to ping exactly n times.

func (*Pinger) PingNTimeout

func (p *Pinger) PingNTimeout(dst net.IP, n int, timeout time.Duration,
	opts ...unixx.SockOpt) (Replies, error)

PingNTimeout is like PingNPayloadTimeout, but wuth no payload.

func (*Pinger) PingPayload added in v1.1.0

func (p *Pinger) PingPayload(dst net.IP, payload []byte, opts ...unixx.SockOpt) (Reply, error)

PingPayload is like PingContextPayload, but with background context.

func (*Pinger) PingPayloadTimeout added in v1.1.0

func (p *Pinger) PingPayloadTimeout(dst net.IP, payload []byte,
	timeout time.Duration, opts ...unixx.SockOpt) (Reply, error)

PingPayloadTimeout is like PingContextPayloadTimeout, but with background context.

func (*Pinger) PingTimeout

func (p *Pinger) PingTimeout(dst net.IP, timeout time.Duration, opts ...unixx.SockOpt) (Reply, error)

PingTimeout is like PingPayloadTimeout, but no payload.

func (*Pinger) Send added in v1.2.0

func (p *Pinger) Send(typ icmp.Type, code uint8, body icmp.MessageBody,
	dst net.IP, opts ...unixx.SockOpt) error

Send just sends ICMP packet with given type, code and body to dst, ignoring sequence number management and timestamping, so it would not interfere with Ping* methods. opts can be used to set per-packet sendmsg(2) options.

func (*Pinger) Set added in v1.1.0

func (p *Pinger) Set(opts ...unixx.SockOpt) error

Set sets given options on the underlying socket with setsockopt(2)

type Replies added in v1.1.0

type Replies []Reply

func (Replies) AvgRTT added in v1.1.0

func (rs Replies) AvgRTT() time.Duration

AvgRTT returns average RTT across successfull replies.

func (Replies) MaxRTT added in v1.2.0

func (rs Replies) MaxRTT() time.Duration

MaxRTT returns maximum RTT across successfull replies.

func (Replies) MinRTT added in v1.2.0

func (rs Replies) MinRTT() time.Duration

MinRTT returns minimum RTT across successfull replies.

type Reply

type Reply struct {
	// From is the sender IP address of recevied reply.
	From net.IP

	// RTT is a round trip time: the time interval between sending
	// an ICMP Echo Request and receiving ICMP Echo Reply.
	RTT time.Duration

	// TTL is time-to-live field from the recieved IP packet
	TTL uint8

	// Data is a reply payload
	Data []byte

	// Err is not nil if ICMP error was received.
	// Other fields are valid even if Err is not nil.
	Err ICMPError
}

type TimeExceededError added in v1.1.0

type TimeExceededError net.IP

func NewTimeExceededError added in v1.1.0

func NewTimeExceededError(from net.IP) TimeExceededError

func (TimeExceededError) Error added in v1.1.0

func (e TimeExceededError) Error() string

func (TimeExceededError) From added in v1.1.0

func (e TimeExceededError) From() net.IP

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL