mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-24 17:48:57 +00:00
netcheck: work behind UDP-blocked networks again, add tests
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
a87ee4168a
commit
b9c6d3ceb8
@@ -142,7 +142,7 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
// Mask user context with ours that we guarantee to cancel so
|
||||
// we can depend on it being closed in goroutines later.
|
||||
// (User ctx might be context.Background, etc)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if c.DERP == nil {
|
||||
@@ -222,6 +222,16 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
gotEP4 string
|
||||
bestDerpLatency time.Duration
|
||||
)
|
||||
anyV6 := func() bool {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return ret.IPv6
|
||||
}
|
||||
anyV4 := func() bool {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return gotEP4 != ""
|
||||
}
|
||||
add := func(server, ipPort string, d time.Duration) {
|
||||
c.logf("%s says we are %s (in %v)", server, ipPort, d)
|
||||
|
||||
@@ -331,14 +341,10 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
|
||||
grp.Go(func() error {
|
||||
err := s4.Run(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
// If we got at least one IPv4 endpoint, treat that as
|
||||
// good enough.
|
||||
if gotEP4 != "" {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
if !anyV4() {
|
||||
c.logf("netcheck: no IPv4 UDP STUN replies")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -362,12 +368,17 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
c.mu.Unlock()
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := s6.Run(ctx); err != nil {
|
||||
// IPv6 seemed like it was configured, but actually failed.
|
||||
// Just log and return a nil error.
|
||||
c.logf("netcheck: ignoring IPv6 failure: %v", err)
|
||||
err := s6.Run(ctx)
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
if !anyV6() {
|
||||
// IPv6 seemed like it was configured, but actually failed.
|
||||
// Just log and return a nil error.
|
||||
c.logf("netcheck: IPv6 seemed configured, but no UDP STUN replies")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
// Otherwise must be some invalid use of Stunner.
|
||||
return err //
|
||||
})
|
||||
if c.GetSTUNConn6 == nil {
|
||||
go reader(s6, pc6)
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
package netcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"tailscale.com/derp/derpmap"
|
||||
"tailscale.com/stun"
|
||||
"tailscale.com/stun/stuntest"
|
||||
)
|
||||
|
||||
func TestHairpinSTUN(t *testing.T) {
|
||||
@@ -29,3 +35,66 @@ func TestHairpinSTUN(t *testing.T) {
|
||||
t.Fatal("expected value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
stunAddr, cleanup := stuntest.Serve(t)
|
||||
defer cleanup()
|
||||
|
||||
c := &Client{
|
||||
DERP: derpmap.NewTestWorld(stunAddr),
|
||||
Logf: t.Logf,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
r, err := c.GetReport(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !r.UDP {
|
||||
t.Error("want UDP")
|
||||
}
|
||||
if len(r.DERPLatency) != 1 {
|
||||
t.Errorf("expected 1 key in DERPLatency; got %+v", r.DERPLatency)
|
||||
}
|
||||
if _, ok := r.DERPLatency[stunAddr]; !ok {
|
||||
t.Errorf("expected key %q in DERPLatency; got %+v", stunAddr, r.DERPLatency)
|
||||
}
|
||||
if r.GlobalV4 == "" {
|
||||
t.Error("expected GlobalV4 set")
|
||||
}
|
||||
if r.PreferredDERP != 1 {
|
||||
t.Errorf("PreferredDERP = %v; want 1", r.PreferredDERP)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorksWhenUDPBlocked(t *testing.T) {
|
||||
blackhole, err := net.ListenPacket("udp4", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open blackhole STUN listener: %v", err)
|
||||
}
|
||||
defer blackhole.Close()
|
||||
|
||||
stunAddr := blackhole.LocalAddr().String()
|
||||
stunAddr = strings.Replace(stunAddr, "0.0.0.0:", "127.0.0.1:", 1)
|
||||
|
||||
c := &Client{
|
||||
DERP: derpmap.NewTestWorld(stunAddr),
|
||||
Logf: t.Logf,
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
r, err := c.GetReport(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := &Report{
|
||||
DERPLatency: map[string]time.Duration{},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r, want) {
|
||||
t.Errorf("mismatch\n got: %+v\nwant: %+v\n", r, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user