mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
netcheck: DERP latency over HTTPS when UDP is blocked
* netcheck: DERP letency over HTTPS when UDP failed Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: async DERP latency check over HTTPS Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: DERP latency check over HTTPS: fix concurrent map Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: DERP latency check over HTTPS: some improvements Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: DERP latency check over HTTPS: use timeout context Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: DERP latency check over HTTPS: use report mutex Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: DERP latency check over HTTPS if UDP is BLOCKED Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com> * netcheck: DERP latency check over HTTPS: new function measureHTTPSLatency Updates #207 Signed-off-by: Zijie Lu <zijie@tailscale.com>
This commit is contained in:
parent
8a3e77fc43
commit
874be6566d
1
go.mod
1
go.mod
@ -19,6 +19,7 @@ require (
|
||||
github.com/peterbourgon/ff/v2 v2.0.0
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f
|
||||
github.com/tailscale/wireguard-go v0.0.0-20200424121617-8d10f231531a
|
||||
github.com/tcnksm/go-httpstat v0.2.0
|
||||
github.com/toqueteos/webbrowser v1.2.0
|
||||
go4.org/mem v0.0.0-20200411205429-f77f31c81751
|
||||
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6
|
||||
|
2
go.sum
2
go.sum
@ -81,6 +81,8 @@ github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f h1:uFj5bslHs
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f/go.mod h1:x880GWw5fvrl2DVTQ04ttXQD4DuppTt1Yz6wLibbjNE=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20200424121617-8d10f231531a h1:HMkTFyhcvZaKf7+7T76rks4HqB83fptUemBIfLGI6TM=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20200424121617-8d10f231531a/go.mod h1:JPm5cTfu1K+qDFRbiHy0sOlHUylYQbpl356sdYFD8V4=
|
||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
||||
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
||||
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
|
||||
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
|
||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||
|
@ -11,12 +11,15 @@
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tcnksm/go-httpstat"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"tailscale.com/derp/derpmap"
|
||||
"tailscale.com/net/dnscache"
|
||||
@ -442,8 +445,6 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
// Check hairpinning.
|
||||
if ret.MappingVariesByDestIP == "false" && gotEP4 != "" {
|
||||
select {
|
||||
@ -453,12 +454,32 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
ret.HairPinning.Set(false)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
// TODO: if UDP is blocked, try to measure TCP connect times
|
||||
// to DERP nodes instead? So UDP-blocked users still get a
|
||||
// decent DERP node, rather than being randomly assigned to
|
||||
// the other side of the planet? Or try ICMP? (likely also
|
||||
// blocked?)
|
||||
// Try HTTPS latency check if UDP is blocked and all checkings failed
|
||||
if !anyV4() {
|
||||
c.logf("netcheck: UDP is blocked, try HTTPS")
|
||||
var wg sync.WaitGroup
|
||||
for _, server := range stuns4 {
|
||||
server := server
|
||||
if _, ok := ret.DERPLatency[server]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if d, err := c.measureHTTPSLatency(server); err != nil {
|
||||
c.logf("netcheck: measuring HTTPS latency of %v: %v", server, err)
|
||||
} else {
|
||||
mu.Lock()
|
||||
ret.DERPLatency[server] = d
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
report := ret.Clone()
|
||||
|
||||
@ -468,6 +489,39 @@ func (c *Client) GetReport(ctx context.Context) (*Report, error) {
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (c *Client) measureHTTPSLatency(server string) (time.Duration, error) {
|
||||
host, _, err := net.SplitHostPort(server)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var result httpstat.Result
|
||||
hctx, cancel := context.WithTimeout(httpstat.WithHTTPStat(context.Background(), &result), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
u := fmt.Sprintf("https://%s/derp/latency-check", host)
|
||||
req, err := http.NewRequestWithContext(hctx, "GET", u, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = io.Copy(ioutil.Discard, resp.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
result.End(c.timeNow())
|
||||
|
||||
// TODO: decide best timing heuristic here.
|
||||
// Maybe the server should return the tcpinfo_rtt?
|
||||
return result.ServerProcessing, nil
|
||||
}
|
||||
|
||||
func (c *Client) logConciseReport(r *Report) {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 256)) // empirically: 5 DERPs + IPv6 == ~233 bytes
|
||||
fmt.Fprintf(buf, "udp=%v", r.UDP)
|
||||
|
Loading…
Reference in New Issue
Block a user