AwaitReachable pings the provided IP continually until the address is reachable. It's the callers responsibility to provide a timeout, otherwise this function will block forever.
(ctx context.Context, ip netip.Addr)
| 605 | // address is reachable. It's the callers responsibility to provide |
| 606 | // a timeout, otherwise this function will block forever. |
| 607 | func (c *Conn) AwaitReachable(ctx context.Context, ip netip.Addr) bool { |
| 608 | ctx, cancel := context.WithCancel(ctx) |
| 609 | defer cancel() // Cancel all pending pings on exit. |
| 610 | |
| 611 | completedCtx, completed := context.WithCancel(context.Background()) |
| 612 | defer completed() |
| 613 | |
| 614 | run := func() { |
| 615 | // Safety timeout, initially we'll have around 10-20 goroutines |
| 616 | // running in parallel. The exponential backoff will converge |
| 617 | // around ~1 ping / 30s, this means we'll have around 10-20 |
| 618 | // goroutines pending towards the end as well. |
| 619 | ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) |
| 620 | defer cancel() |
| 621 | |
| 622 | // For reachability, we use TSMP ping, which pings at the IP layer, and |
| 623 | // therefore requires that wireguard and the netstack are up. If we |
| 624 | // don't wait for wireguard to be up, we could miss a handshake, and it |
| 625 | // might take 5 seconds for the handshake to be retried. A 5s initial |
| 626 | // round trip can set us up for poor TCP performance, since the initial |
| 627 | // round-trip-time sets the initial retransmit timeout. |
| 628 | _, _, _, err := c.pingWithType(ctx, ip, tailcfg.PingTSMP) |
| 629 | if err == nil { |
| 630 | completed() |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | eb := backoff.NewExponentialBackOff() |
| 635 | eb.MaxElapsedTime = 0 |
| 636 | eb.InitialInterval = 50 * time.Millisecond |
| 637 | eb.MaxInterval = 30 * time.Second |
| 638 | // Consume the first interval since |
| 639 | // we'll fire off a ping immediately. |
| 640 | _ = eb.NextBackOff() |
| 641 | |
| 642 | t := backoff.NewTicker(eb) |
| 643 | defer t.Stop() |
| 644 | |
| 645 | go run() |
| 646 | for { |
| 647 | select { |
| 648 | case <-completedCtx.Done(): |
| 649 | return true |
| 650 | case <-t.C: |
| 651 | // Pings can take a while, so we can run multiple |
| 652 | // in parallel to return ASAP. |
| 653 | go run() |
| 654 | case <-ctx.Done(): |
| 655 | return false |
| 656 | } |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | // Closed is a channel that ends when the connection has |
| 661 | // been closed. |