mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-21 06:01:42 +00:00
tailcfg: add IP and Types field to PingRequest
Signed-off-by: Simeng He <simeng@tailscale.com>
This commit is contained in:
parent
d5e1abd0c4
commit
e199e407d2
@ -1302,3 +1302,59 @@ func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tsmpPing sends a Ping to pr.IP, and sends an http request back to pr.URL
|
||||||
|
// with ping response data.
|
||||||
|
func tsmpPing(logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest, pinger Pinger) error {
|
||||||
|
var err error
|
||||||
|
if pr.URL == "" {
|
||||||
|
return errors.New("invalid PingRequest with no URL")
|
||||||
|
}
|
||||||
|
if pr.IP.IsZero() {
|
||||||
|
return fmt.Errorf("PingRequest with no proper IP got %v", pr.IP)
|
||||||
|
}
|
||||||
|
if !strings.Contains(pr.Types, "TSMP") {
|
||||||
|
return fmt.Errorf("PingRequest with no TSMP in Types, got : %v", pr.Types)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
pinger.Ping(pr.IP, true, func(res *ipnstate.PingResult) {
|
||||||
|
// Currently does not check for error since we just return if it fails.
|
||||||
|
err = postPingResult(now, logf, c, pr, res)
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func postPingResult(now time.Time, logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest, res *ipnstate.PingResult) error {
|
||||||
|
if res.Err != "" {
|
||||||
|
return errors.New(res.Err)
|
||||||
|
}
|
||||||
|
duration := time.Since(now)
|
||||||
|
if pr.Log {
|
||||||
|
logf("TSMP ping to %v completed in %v seconds. pinger.Ping took %v seconds", pr.IP, res.LatencySeconds, duration.Seconds())
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
jsonPingRes, err := json.Marshal(res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Send the results of the Ping, back to control URL.
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", pr.URL, bytes.NewBuffer(jsonPingRes))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("http.NewRequestWithContext(%q): %w", pr.URL, err)
|
||||||
|
}
|
||||||
|
if pr.Log {
|
||||||
|
logf("tsmpPing: sending ping results to %v ...", pr.URL)
|
||||||
|
}
|
||||||
|
t0 := time.Now()
|
||||||
|
_, err = c.Do(req)
|
||||||
|
d := time.Since(t0).Round(time.Millisecond)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("tsmpPing error: %w to %v (after %v)", err, pr.URL, d)
|
||||||
|
} else if pr.Log {
|
||||||
|
logf("tsmpPing complete to %v (after %v)", pr.URL, d)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -6,9 +6,13 @@ package controlclient
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/wgkey"
|
"tailscale.com/types/wgkey"
|
||||||
)
|
)
|
||||||
@ -103,3 +107,56 @@ func TestNewHostinfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("Got: %s", j)
|
t.Logf("Got: %s", j)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTsmpPing(t *testing.T) {
|
||||||
|
hi := NewHostinfo()
|
||||||
|
ni := tailcfg.NetInfo{LinkType: "wired"}
|
||||||
|
hi.NetInfo = &ni
|
||||||
|
|
||||||
|
key, err := wgkey.NewPrivate()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
opts := Options{
|
||||||
|
ServerURL: "https://example.com",
|
||||||
|
Hostinfo: hi,
|
||||||
|
GetMachinePrivateKey: func() (wgkey.Private, error) {
|
||||||
|
return key, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := NewDirect(opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pingRes := &ipnstate.PingResult{
|
||||||
|
IP: "123.456.7890",
|
||||||
|
Err: "",
|
||||||
|
NodeName: "testnode",
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
body := new(ipnstate.PingResult)
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(body); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if pingRes.IP != body.IP {
|
||||||
|
t.Fatalf("PingResult did not have the correct IP : got %v, expected : %v", body.IP, pingRes.IP)
|
||||||
|
}
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
pr := &tailcfg.PingRequest{
|
||||||
|
URL: ts.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = postPingResult(now, t.Logf, c.httpc, pr, pingRes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -851,6 +851,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
DiscoPublicKey: discoPublic,
|
DiscoPublicKey: discoPublic,
|
||||||
DebugFlags: debugFlags,
|
DebugFlags: debugFlags,
|
||||||
LinkMonitor: b.e.GetLinkMonitor(),
|
LinkMonitor: b.e.GetLinkMonitor(),
|
||||||
|
Pinger: b.e,
|
||||||
|
|
||||||
// Don't warn about broken Linux IP forwading when
|
// Don't warn about broken Linux IP forwading when
|
||||||
// netstack is being used.
|
// netstack is being used.
|
||||||
|
@ -897,19 +897,30 @@ type DNSRecord struct {
|
|||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PingRequest is a request to send an HTTP request to prove the
|
// PingRequest with no IP and Types is a request to send an HTTP request to prove the
|
||||||
// long-polling client is still connected.
|
// long-polling client is still connected.
|
||||||
|
// PingRequest with Types and IP, will send a ping to the IP and send a
|
||||||
|
// POST request to the URL to prove that the ping succeeded.
|
||||||
type PingRequest struct {
|
type PingRequest struct {
|
||||||
// URL is the URL to send a HEAD request to.
|
// URL is the URL to send a HEAD request to.
|
||||||
// It will be a unique URL each time. No auth headers are necessary.
|
// It will be a unique URL each time. No auth headers are necessary.
|
||||||
//
|
//
|
||||||
// If the client sees multiple PingRequests with the same URL,
|
// If the client sees multiple PingRequests with the same URL,
|
||||||
// subsequent ones should be ignored.
|
// subsequent ones should be ignored.
|
||||||
|
// If Types and IP are defined, then URL is the URL to send a POST request to.
|
||||||
URL string
|
URL string
|
||||||
|
|
||||||
// Log is whether to log about this ping in the success case.
|
// Log is whether to log about this ping in the success case.
|
||||||
// For failure cases, the client will log regardless.
|
// For failure cases, the client will log regardless.
|
||||||
Log bool `json:",omitempty"`
|
Log bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// Types is the types of ping that is initiated. Can be TSMP, ICMP or disco.
|
||||||
|
// Types will be comma separated, such as TSMP,disco.
|
||||||
|
Types string
|
||||||
|
|
||||||
|
// IP is the ping target.
|
||||||
|
// It is used in TSMP pings, if IP is invalid or empty then do a HEAD request to the URL.
|
||||||
|
IP netaddr.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapResponse struct {
|
type MapResponse struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user