func (t *Tracer) Trace(ctx context.Context, ip net.IP, h func(reply *Reply)) error {
	sess, err := t.NewSession(ip)
	if err != nil {
		return err
	}
	defer sess.Close()
	delay := time.NewTicker(t.Delay)
	defer delay.Stop()
	max := t.MaxHops
	for n := 0; n < t.Count; n++ {
		for ttl := 1; ttl <= t.MaxHops && ttl <= max; ttl++ {
			err = sess.Ping(ttl)
			if err != nil {
				return err
			}
			select {
			case <-delay.C:
			case r := <-sess.Receive():
				if max > r.Hops && ip.Equal(r.IP) {
					max = r.Hops
				}
				h(r)
			case <-ctx.Done():
				return ctx.Err()
			}
		}
	}
	if sess.isDone(max) {
		return nil
	}
	deadline := time.After(t.Timeout)
	for {
		select {
		case r := <-sess.Receive():
			if max > r.Hops && ip.Equal(r.IP) {
				max = r.Hops
			}
			h(r)
			if sess.isDone(max) {
				return nil
			}
		case <-deadline:
			return nil
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}
Trace starts sending IP packets increasing TTL until MaxHops and calls h for each reply.
package traceroute
import (
	"context"
	"errors"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"net"
	"sort"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
)
var DefaultConfig = Config{
	Delay:    50 * time.Millisecond,
	Timeout:  2 * time.Second,
	MaxHops:  30,
	Count:    3,
	Networks: []string{"ip4:icmp", "ip4:ip"},
}
var DefaultTracer = &Tracer{
	Config: DefaultConfig,
}
type Config struct {
	Delay    time.Duration
	Timeout  time.Duration
	MaxHops  int
	Count    int
	Networks []string
	Addr     *net.IPAddr
}
type Tracer struct {
	Config
	once sync.Once
	conn *net.IPConn
	err  error
	mu   sync.RWMutex
	sess map[string][]*Session
	seq  uint32
}