2023-01-27 21:37:20 +00:00
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
2021-07-13 05:04:16 +00:00
// The derpprobe binary probes derpers.
2023-01-27 14:49:50 +00:00
package main
2021-07-13 05:04:16 +00:00
import (
"flag"
"fmt"
"log"
"net/http"
"sort"
"time"
2023-01-27 14:49:50 +00:00
"tailscale.com/prober"
"tailscale.com/tsweb"
2024-04-02 18:55:33 +00:00
"tailscale.com/version"
2021-07-13 05:04:16 +00:00
)
var (
2024-06-06 19:30:54 +00:00
derpMapURL = flag . String ( "derp-map" , "https://login.tailscale.com/derpmap/default" , "URL to DERP map (https:// or file://) or 'local' to use the local tailscaled's DERP map" )
2024-04-02 18:55:33 +00:00
versionFlag = flag . Bool ( "version" , false , "print version and exit" )
2024-02-28 20:27:44 +00:00
listen = flag . String ( "listen" , ":8030" , "HTTP listen address" )
probeOnce = flag . Bool ( "once" , false , "probe once and print results, then exit; ignores the listen flag" )
spread = flag . Bool ( "spread" , true , "whether to spread probing over time" )
interval = flag . Duration ( "interval" , 15 * time . Second , "probe interval" )
meshInterval = flag . Duration ( "mesh-interval" , 15 * time . Second , "mesh probe interval" )
stunInterval = flag . Duration ( "stun-interval" , 15 * time . Second , "STUN probe interval" )
tlsInterval = flag . Duration ( "tls-interval" , 15 * time . Second , "TLS probe interval" )
bwInterval = flag . Duration ( "bw-interval" , 0 , "bandwidth probe interval (0 = no bandwidth probing)" )
bwSize = flag . Int64 ( "bw-probe-size-bytes" , 1_000_000 , "bandwidth probe size" )
2021-07-13 05:04:16 +00:00
)
func main ( ) {
flag . Parse ( )
2024-04-02 18:55:33 +00:00
if * versionFlag {
fmt . Println ( version . Long ( ) )
return
}
2022-03-01 04:13:33 +00:00
2023-04-03 10:35:12 +00:00
p := prober . New ( ) . WithSpread ( * spread ) . WithOnce ( * probeOnce ) . WithMetricNamespace ( "derpprobe" )
2024-02-28 20:27:44 +00:00
opts := [ ] prober . DERPOpt {
prober . WithMeshProbing ( * meshInterval ) ,
prober . WithSTUNProbing ( * stunInterval ) ,
prober . WithTLSProbing ( * tlsInterval ) ,
}
if * bwInterval > 0 {
opts = append ( opts , prober . WithBandwidthProbing ( * bwInterval , * bwSize ) )
}
dp , err := prober . DERP ( p , * derpMapURL , opts ... )
2023-01-27 14:49:50 +00:00
if err != nil {
log . Fatal ( err )
}
p . Run ( "derpmap-probe" , * interval , nil , dp . ProbeMap )
2022-03-01 04:13:33 +00:00
2022-11-24 00:09:24 +00:00
if * probeOnce {
2023-01-27 14:49:50 +00:00
log . Printf ( "Waiting for all probes (may take up to 1m)" )
p . Wait ( )
st := getOverallStatus ( p )
2022-11-24 00:09:24 +00:00
for _ , s := range st . good {
log . Printf ( "good: %s" , s )
}
for _ , s := range st . bad {
log . Printf ( "bad: %s" , s )
}
return
}
2023-01-27 14:49:50 +00:00
mux := http . NewServeMux ( )
2024-07-31 08:03:14 +00:00
d := tsweb . Debugger ( mux )
d . Handle ( "probe-run" , "Run a probe" , tsweb . StdHandler ( tsweb . ReturnHandlerFunc ( p . RunHandler ) , tsweb . HandlerOptions { Logf : log . Printf } ) )
mux . Handle ( "/" , tsweb . StdHandler ( p . StatusHandler (
prober . WithTitle ( "DERP Prober" ) ,
prober . WithPageLink ( "Prober metrics" , "/debug/varz" ) ,
prober . WithProbeLink ( "Run Probe" , "/debug/probe-run?name={{.Name}}" ) ,
) , tsweb . HandlerOptions { Logf : log . Printf } ) )
2024-10-15 20:18:04 +00:00
mux . Handle ( "/healthz" , http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "text/plain" )
w . WriteHeader ( http . StatusOK )
w . Write ( [ ] byte ( "ok\n" ) )
} ) )
2024-02-28 20:27:44 +00:00
log . Printf ( "Listening on %s" , * listen )
2023-01-27 14:49:50 +00:00
log . Fatal ( http . ListenAndServe ( * listen , mux ) )
2022-01-26 19:58:56 +00:00
}
2021-07-13 05:04:16 +00:00
type overallStatus struct {
good , bad [ ] string
}
2022-03-16 23:27:57 +00:00
func ( st * overallStatus ) addBadf ( format string , a ... any ) {
2021-07-13 05:04:16 +00:00
st . bad = append ( st . bad , fmt . Sprintf ( format , a ... ) )
}
2022-03-16 23:27:57 +00:00
func ( st * overallStatus ) addGoodf ( format string , a ... any ) {
2021-07-13 05:04:16 +00:00
st . good = append ( st . good , fmt . Sprintf ( format , a ... ) )
}
2023-01-27 14:49:50 +00:00
func getOverallStatus ( p * prober . Prober ) ( o overallStatus ) {
for p , i := range p . ProbeInfo ( ) {
if i . End . IsZero ( ) {
// Do not show probes that have not finished yet.
2022-01-26 19:58:56 +00:00
continue
}
2023-01-27 14:49:50 +00:00
if i . Result {
o . addGoodf ( "%s: %s" , p , i . Latency )
} else {
o . addBadf ( "%s: %s" , p , i . Error )
2022-01-26 19:58:56 +00:00
}
}
2023-01-27 14:49:50 +00:00
sort . Strings ( o . bad )
sort . Strings ( o . good )
2021-07-13 05:04:16 +00:00
return
}