diff --git a/net/netcheck/netcheck.go b/net/netcheck/netcheck.go index 69236e773..272f2333d 100644 --- a/net/netcheck/netcheck.go +++ b/net/netcheck/netcheck.go @@ -161,6 +161,10 @@ type Client struct { // in tests to avoid probing the local LAN's router, etc. SkipExternalNetwork bool + // UDPBindAddr, if non-empty, is the address to listen on for UDP. + // It defaults to ":0". + UDPBindAddr string + mu sync.Mutex // guards following nextFull bool // do a full region scan, even if last != nil prev map[time.Time]*Report // some previous reports @@ -777,6 +781,13 @@ func newReport() *Report { } } +func (c *Client) udpBindAddr() string { + if v := c.UDPBindAddr; v != "" { + return v + } + return ":0" +} + // GetReport gets a report. // // It may not be called concurrently with itself. @@ -857,7 +868,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (*Report, e if f := c.GetSTUNConn4; f != nil { rs.pc4 = f() } else { - u4, err := netns.Listener().ListenPacket(ctx, "udp4", ":0") + u4, err := netns.Listener().ListenPacket(ctx, "udp4", c.udpBindAddr()) if err != nil { c.logf("udp4: %v", err) return nil, err @@ -870,7 +881,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (*Report, e if f := c.GetSTUNConn6; f != nil { rs.pc6 = f() } else { - u6, err := netns.Listener().ListenPacket(ctx, "udp6", ":0") + u6, err := netns.Listener().ListenPacket(ctx, "udp6", c.udpBindAddr()) if err != nil { c.logf("udp6: %v", err) } else { diff --git a/net/netcheck/netcheck_test.go b/net/netcheck/netcheck_test.go index b8f4bfbf5..04082efe8 100644 --- a/net/netcheck/netcheck_test.go +++ b/net/netcheck/netcheck_test.go @@ -50,7 +50,8 @@ func TestBasic(t *testing.T) { defer cleanup() c := &Client{ - Logf: t.Logf, + Logf: t.Logf, + UDPBindAddr: "127.0.0.1:0", } ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)