mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-20 11:58:39 +00:00
net/packet, wgengine/{filter,tstun}: add TSMP ping
Fixes #1467 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
4b77eca2de
commit
2384c112c9
@ -48,6 +48,7 @@ relay node.
|
|||||||
fs := flag.NewFlagSet("ping", flag.ExitOnError)
|
fs := flag.NewFlagSet("ping", flag.ExitOnError)
|
||||||
fs.BoolVar(&pingArgs.verbose, "verbose", false, "verbose output")
|
fs.BoolVar(&pingArgs.verbose, "verbose", false, "verbose output")
|
||||||
fs.BoolVar(&pingArgs.untilDirect, "until-direct", true, "stop once a direct path is established")
|
fs.BoolVar(&pingArgs.untilDirect, "until-direct", true, "stop once a direct path is established")
|
||||||
|
fs.BoolVar(&pingArgs.tsmp, "tsmp", false, "do a TSMP-level ping (through IP + wireguard, but not involving host OS stack)")
|
||||||
fs.IntVar(&pingArgs.num, "c", 10, "max number of pings to send")
|
fs.IntVar(&pingArgs.num, "c", 10, "max number of pings to send")
|
||||||
fs.DurationVar(&pingArgs.timeout, "timeout", 5*time.Second, "timeout before giving up on a ping")
|
fs.DurationVar(&pingArgs.timeout, "timeout", 5*time.Second, "timeout before giving up on a ping")
|
||||||
return fs
|
return fs
|
||||||
@ -58,6 +59,7 @@ var pingArgs struct {
|
|||||||
num int
|
num int
|
||||||
untilDirect bool
|
untilDirect bool
|
||||||
verbose bool
|
verbose bool
|
||||||
|
tsmp bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ func runPing(ctx context.Context, args []string) error {
|
|||||||
anyPong := false
|
anyPong := false
|
||||||
for {
|
for {
|
||||||
n++
|
n++
|
||||||
bc.Ping(ip)
|
bc.Ping(ip, pingArgs.tsmp)
|
||||||
timer := time.NewTimer(pingArgs.timeout)
|
timer := time.NewTimer(pingArgs.timeout)
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
@ -135,8 +137,16 @@ func runPing(ctx context.Context, args []string) error {
|
|||||||
if pr.DERPRegionID != 0 {
|
if pr.DERPRegionID != 0 {
|
||||||
via = fmt.Sprintf("DERP(%s)", pr.DERPRegionCode)
|
via = fmt.Sprintf("DERP(%s)", pr.DERPRegionCode)
|
||||||
}
|
}
|
||||||
|
if pingArgs.tsmp {
|
||||||
|
// TODO(bradfitz): populate the rest of ipnstate.PingResult for TSMP queries?
|
||||||
|
// For now just say it came via TSMP.
|
||||||
|
via = "TSMP"
|
||||||
|
}
|
||||||
anyPong = true
|
anyPong = true
|
||||||
fmt.Printf("pong from %s (%s) via %v in %v\n", pr.NodeName, pr.NodeIP, via, latency)
|
fmt.Printf("pong from %s (%s) via %v in %v\n", pr.NodeName, pr.NodeIP, via, latency)
|
||||||
|
if pingArgs.tsmp {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if pr.Endpoint != "" && pingArgs.untilDirect {
|
if pr.Endpoint != "" && pingArgs.untilDirect {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -165,5 +165,5 @@ type Backend interface {
|
|||||||
// Ping attempts to start connecting to the given IP and sends a Notify
|
// Ping attempts to start connecting to the given IP and sends a Notify
|
||||||
// with its PingResult. If the host is down, there might never
|
// with its PingResult. If the host is down, there might never
|
||||||
// be a PingResult sent. The cmd/tailscale CLI client adds a timeout.
|
// be a PingResult sent. The cmd/tailscale CLI client adds a timeout.
|
||||||
Ping(ip string)
|
Ping(ip string, useTSMP bool)
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,6 @@ func (b *FakeBackend) FakeExpireAfter(x time.Duration) {
|
|||||||
b.notify(Notify{NetMap: &netmap.NetworkMap{}})
|
b.notify(Notify{NetMap: &netmap.NetworkMap{}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *FakeBackend) Ping(ip string) {
|
func (b *FakeBackend) Ping(ip string, useTSMP bool) {
|
||||||
b.notify(Notify{PingResult: &ipnstate.PingResult{}})
|
b.notify(Notify{PingResult: &ipnstate.PingResult{}})
|
||||||
}
|
}
|
||||||
|
@ -1173,13 +1173,13 @@ func (b *LocalBackend) FakeExpireAfter(x time.Duration) {
|
|||||||
b.send(ipn.Notify{NetMap: b.netMap})
|
b.send(ipn.Notify{NetMap: b.netMap})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) Ping(ipStr string) {
|
func (b *LocalBackend) Ping(ipStr string, useTSMP bool) {
|
||||||
ip, err := netaddr.ParseIP(ipStr)
|
ip, err := netaddr.ParseIP(ipStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.logf("ignoring Ping request to invalid IP %q", ipStr)
|
b.logf("ignoring Ping request to invalid IP %q", ipStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.e.Ping(ip, func(pr *ipnstate.PingResult) {
|
b.e.Ping(ip, useTSMP, func(pr *ipnstate.PingResult) {
|
||||||
b.send(ipn.Notify{PingResult: pr})
|
b.send(ipn.Notify{PingResult: pr})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -407,10 +407,18 @@ type PingResult struct {
|
|||||||
Err string
|
Err string
|
||||||
LatencySeconds float64
|
LatencySeconds float64
|
||||||
|
|
||||||
Endpoint string // ip:port if direct UDP was used
|
// Endpoint is the ip:port if direct UDP was used.
|
||||||
|
// It is not currently set for TSMP pings.
|
||||||
|
Endpoint string
|
||||||
|
|
||||||
DERPRegionID int // non-zero if DERP was used
|
// DERPRegionID is non-zero DERP region ID if DERP was used.
|
||||||
DERPRegionCode string // three-letter airport/region code if DERP was used
|
// It is not currently set for TSMP pings.
|
||||||
|
DERPRegionID int
|
||||||
|
|
||||||
|
// DERPRegionCode is the three-letter region code
|
||||||
|
// corresponding to DERPRegionID.
|
||||||
|
// It is not currently set for TSMP pings.
|
||||||
|
DERPRegionCode string
|
||||||
|
|
||||||
// TODO(bradfitz): details like whether port mapping was used on either side? (Once supported)
|
// TODO(bradfitz): details like whether port mapping was used on either side? (Once supported)
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ type FakeExpireAfterArgs struct {
|
|||||||
|
|
||||||
type PingArgs struct {
|
type PingArgs struct {
|
||||||
IP string
|
IP string
|
||||||
|
UseTSMP bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command is a command message that is JSON encoded and sent by a
|
// Command is a command message that is JSON encoded and sent by a
|
||||||
@ -174,7 +175,7 @@ func (bs *BackendServer) GotCommand(ctx context.Context, cmd *Command) error {
|
|||||||
bs.b.RequestEngineStatus()
|
bs.b.RequestEngineStatus()
|
||||||
return nil
|
return nil
|
||||||
} else if c := cmd.Ping; c != nil {
|
} else if c := cmd.Ping; c != nil {
|
||||||
bs.b.Ping(c.IP)
|
bs.b.Ping(c.IP, c.UseTSMP)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,8 +321,11 @@ func (bc *BackendClient) FakeExpireAfter(x time.Duration) {
|
|||||||
bc.send(Command{FakeExpireAfter: &FakeExpireAfterArgs{Duration: x}})
|
bc.send(Command{FakeExpireAfter: &FakeExpireAfterArgs{Duration: x}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BackendClient) Ping(ip string) {
|
func (bc *BackendClient) Ping(ip string, useTSMP bool) {
|
||||||
bc.send(Command{Ping: &PingArgs{IP: ip}})
|
bc.send(Command{Ping: &PingArgs{
|
||||||
|
IP: ip,
|
||||||
|
UseTSMP: useTSMP,
|
||||||
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BackendClient) SetWantRunning(v bool) {
|
func (bc *BackendClient) SetWantRunning(v bool) {
|
||||||
|
@ -343,6 +343,19 @@ func (q *Parsed) IP4Header() IP4Header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Parsed) IP6Header() IP6Header {
|
||||||
|
if q.IPVersion != 6 {
|
||||||
|
panic("IP6Header called on non-IPv6 Parsed")
|
||||||
|
}
|
||||||
|
ipid := (binary.BigEndian.Uint32(q.b[:4]) << 12) >> 12
|
||||||
|
return IP6Header{
|
||||||
|
IPID: ipid,
|
||||||
|
IPProto: q.IPProto,
|
||||||
|
Src: q.Src.IP,
|
||||||
|
Dst: q.Dst.IP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (q *Parsed) ICMP4Header() ICMP4Header {
|
func (q *Parsed) ICMP4Header() ICMP4Header {
|
||||||
if q.IPVersion != 4 {
|
if q.IPVersion != 4 {
|
||||||
panic("IP4Header called on non-IPv4 Parsed")
|
panic("IP4Header called on non-IPv4 Parsed")
|
||||||
|
@ -70,6 +70,12 @@ type TSMPType uint8
|
|||||||
const (
|
const (
|
||||||
// TSMPTypeRejectedConn is the type byte for a TailscaleRejectedHeader.
|
// TSMPTypeRejectedConn is the type byte for a TailscaleRejectedHeader.
|
||||||
TSMPTypeRejectedConn TSMPType = '!'
|
TSMPTypeRejectedConn TSMPType = '!'
|
||||||
|
|
||||||
|
// TSMPTypePing is the type byte for a TailscalePingRequest.
|
||||||
|
TSMPTypePing TSMPType = 'p'
|
||||||
|
|
||||||
|
// TSMPTypePong is the type byte for a TailscalePongResponse.
|
||||||
|
TSMPTypePong TSMPType = 'o'
|
||||||
)
|
)
|
||||||
|
|
||||||
type TailscaleRejectReason byte
|
type TailscaleRejectReason byte
|
||||||
@ -195,3 +201,58 @@ func (pp *Parsed) AsTailscaleRejectedHeader() (h TailscaleRejectedHeader, ok boo
|
|||||||
}
|
}
|
||||||
return h, true
|
return h, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TSMPPingRequest is a TSMP message that's like an ICMP ping request.
|
||||||
|
//
|
||||||
|
// On the wire, after the IP header, it's currently 9 bytes:
|
||||||
|
// * 'p' (TSMPTypePing)
|
||||||
|
// * 8 opaque ping bytes to copy back in the response
|
||||||
|
type TSMPPingRequest struct {
|
||||||
|
Data [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *Parsed) AsTSMPPing() (h TSMPPingRequest, ok bool) {
|
||||||
|
if pp.IPProto != ipproto.TSMP {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := pp.Payload()
|
||||||
|
if len(p) < 9 || p[0] != byte(TSMPTypePing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(h.Data[:], p[1:])
|
||||||
|
return h, true
|
||||||
|
}
|
||||||
|
|
||||||
|
type TSMPPongReply struct {
|
||||||
|
IPHeader Header
|
||||||
|
Data [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *Parsed) AsTSMPPong() (data [8]byte, ok bool) {
|
||||||
|
if pp.IPProto != ipproto.TSMP {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := pp.Payload()
|
||||||
|
if len(p) < 9 || p[0] != byte(TSMPTypePong) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(data[:], p[1:])
|
||||||
|
return data, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h TSMPPongReply) Len() int {
|
||||||
|
return h.IPHeader.Len() + 9
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h TSMPPongReply) Marshal(buf []byte) error {
|
||||||
|
if len(buf) < h.Len() {
|
||||||
|
return errSmallBuffer
|
||||||
|
}
|
||||||
|
if err := h.IPHeader.Marshal(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = buf[h.IPHeader.Len():]
|
||||||
|
buf[0] = byte(TSMPTypePong)
|
||||||
|
copy(buf[1:], h.Data[:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -423,6 +423,8 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) {
|
|||||||
if f.matches6.match(q) {
|
if f.matches6.match(q) {
|
||||||
return Accept, "ok"
|
return Accept, "ok"
|
||||||
}
|
}
|
||||||
|
case ipproto.TSMP:
|
||||||
|
return Accept, "tsmp ok"
|
||||||
default:
|
default:
|
||||||
return Drop, "Unknown proto"
|
return Drop, "Unknown proto"
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,9 @@ type TUN struct {
|
|||||||
// PostFilterOut is the outbound filter function that runs after the main filter.
|
// PostFilterOut is the outbound filter function that runs after the main filter.
|
||||||
PostFilterOut FilterFunc
|
PostFilterOut FilterFunc
|
||||||
|
|
||||||
|
// OnTSMPPongReceived, if non-nil, is called whenever a TSMP pong arrives.
|
||||||
|
OnTSMPPongReceived func(data [8]byte)
|
||||||
|
|
||||||
// disableFilter disables all filtering when set. This should only be used in tests.
|
// disableFilter disables all filtering when set. This should only be used in tests.
|
||||||
disableFilter bool
|
disableFilter bool
|
||||||
}
|
}
|
||||||
@ -323,6 +326,18 @@ func (t *TUN) filterIn(buf []byte) filter.Response {
|
|||||||
defer parsedPacketPool.Put(p)
|
defer parsedPacketPool.Put(p)
|
||||||
p.Decode(buf)
|
p.Decode(buf)
|
||||||
|
|
||||||
|
if p.IPProto == ipproto.TSMP {
|
||||||
|
if pingReq, ok := p.AsTSMPPing(); ok {
|
||||||
|
t.noteActivity()
|
||||||
|
t.injectOutboundPong(p, pingReq)
|
||||||
|
return filter.DropSilently
|
||||||
|
} else if data, ok := p.AsTSMPPong(); ok {
|
||||||
|
if f := t.OnTSMPPongReceived; f != nil {
|
||||||
|
f(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if t.PreFilterIn != nil {
|
if t.PreFilterIn != nil {
|
||||||
if res := t.PreFilterIn(p, t); res.IsDrop() {
|
if res := t.PreFilterIn(p, t); res.IsDrop() {
|
||||||
return res
|
return res
|
||||||
@ -440,6 +455,26 @@ func (t *TUN) InjectInboundCopy(packet []byte) error {
|
|||||||
return t.InjectInboundDirect(buf, PacketStartOffset)
|
return t.InjectInboundDirect(buf, PacketStartOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TUN) injectOutboundPong(pp *packet.Parsed, req packet.TSMPPingRequest) {
|
||||||
|
pong := packet.TSMPPongReply{
|
||||||
|
Data: req.Data,
|
||||||
|
}
|
||||||
|
switch pp.IPVersion {
|
||||||
|
case 4:
|
||||||
|
h4 := pp.IP4Header()
|
||||||
|
h4.ToResponse()
|
||||||
|
pong.IPHeader = h4
|
||||||
|
case 6:
|
||||||
|
h6 := pp.IP6Header()
|
||||||
|
h6.ToResponse()
|
||||||
|
pong.IPHeader = h6
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.InjectOutbound(packet.Generate(pong, nil))
|
||||||
|
}
|
||||||
|
|
||||||
// InjectOutbound makes the TUN device behave as if a packet
|
// InjectOutbound makes the TUN device behave as if a packet
|
||||||
// with the given contents was sent to the network.
|
// with the given contents was sent to the network.
|
||||||
// It does not block, but takes ownership of the packet.
|
// It does not block, but takes ownership of the packet.
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -128,6 +129,7 @@ type userspaceEngine struct {
|
|||||||
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
|
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
|
||||||
networkMapCallbacks map[*someHandle]NetworkMapCallback
|
networkMapCallbacks map[*someHandle]NetworkMapCallback
|
||||||
tsIPByIPPort map[netaddr.IPPort]netaddr.IP // allows registration of IP:ports as belonging to a certain Tailscale IP for whois lookups
|
tsIPByIPPort map[netaddr.IPPort]netaddr.IP // allows registration of IP:ports as belonging to a certain Tailscale IP for whois lookups
|
||||||
|
pongCallback map[[8]byte]func() // for TSMP pong responses
|
||||||
|
|
||||||
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
||||||
}
|
}
|
||||||
@ -351,6 +353,16 @@ func newUserspaceEngine(logf logger.Logf, rawTUNDev tun.Device, conf Config) (_
|
|||||||
SkipBindUpdate: true,
|
SkipBindUpdate: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.tundev.OnTSMPPongReceived = func(data [8]byte) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
cb := e.pongCallback[data]
|
||||||
|
e.logf("wgengine: got TSMP pong %02x; cb=%v", data, cb != nil)
|
||||||
|
if cb != nil {
|
||||||
|
go cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// wgdev takes ownership of tundev, will close it when closed.
|
// wgdev takes ownership of tundev, will close it when closed.
|
||||||
e.logf("Creating wireguard device...")
|
e.logf("Creating wireguard device...")
|
||||||
e.wgdev = device.NewDevice(e.tundev, opts)
|
e.wgdev = device.NewDevice(e.tundev, opts)
|
||||||
@ -1342,7 +1354,7 @@ func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
e.magicConn.UpdateStatus(sb)
|
e.magicConn.UpdateStatus(sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *userspaceEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {
|
||||||
res := &ipnstate.PingResult{IP: ip.String()}
|
res := &ipnstate.PingResult{IP: ip.String()}
|
||||||
peer, err := e.peerForIP(ip)
|
peer, err := e.peerForIP(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1357,8 +1369,92 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
|||||||
cb(res)
|
cb(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.logf("ping(%v): sending ping to %v %v ...", ip, peer.Key.ShortString(), peer.ComputedName)
|
pingType := "disco"
|
||||||
|
if useTSMP {
|
||||||
|
pingType = "TSMP"
|
||||||
|
}
|
||||||
|
e.logf("ping(%v): sending %v ping to %v %v ...", ip, pingType, peer.Key.ShortString(), peer.ComputedName)
|
||||||
|
if useTSMP {
|
||||||
|
e.sendTSMPPing(ip, peer, res, cb)
|
||||||
|
} else {
|
||||||
e.magicConn.Ping(peer, res, cb)
|
e.magicConn.Ping(peer, res, cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *userspaceEngine) mySelfIPMatchingFamily(dst netaddr.IP) (src netaddr.IP, err error) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
if e.netMap == nil {
|
||||||
|
return netaddr.IP{}, errors.New("no netmap")
|
||||||
|
}
|
||||||
|
for _, a := range e.netMap.Addresses {
|
||||||
|
if a.IsSingleIP() && a.IP.BitLen() == dst.BitLen() {
|
||||||
|
return a.IP, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(e.netMap.Addresses) == 0 {
|
||||||
|
return netaddr.IP{}, errors.New("no self address in netmap")
|
||||||
|
}
|
||||||
|
return netaddr.IP{}, errors.New("no self address in netmap matching address family")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *userspaceEngine) sendTSMPPing(ip netaddr.IP, peer *tailcfg.Node, res *ipnstate.PingResult, cb func(*ipnstate.PingResult)) {
|
||||||
|
srcIP, err := e.mySelfIPMatchingFamily(ip)
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err.Error()
|
||||||
|
cb(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var iph packet.Header
|
||||||
|
if srcIP.Is4() {
|
||||||
|
iph = packet.IP4Header{
|
||||||
|
IPProto: ipproto.TSMP,
|
||||||
|
Src: srcIP,
|
||||||
|
Dst: ip,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iph = packet.IP6Header{
|
||||||
|
IPProto: ipproto.TSMP,
|
||||||
|
Src: srcIP,
|
||||||
|
Dst: ip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var data [8]byte
|
||||||
|
crand.Read(data[:])
|
||||||
|
|
||||||
|
expireTimer := time.AfterFunc(10*time.Second, func() {
|
||||||
|
e.setTSMPPongCallback(data, nil)
|
||||||
|
})
|
||||||
|
t0 := time.Now()
|
||||||
|
e.setTSMPPongCallback(data, func() {
|
||||||
|
expireTimer.Stop()
|
||||||
|
d := time.Since(t0)
|
||||||
|
res.LatencySeconds = d.Seconds()
|
||||||
|
res.NodeIP = ip.String()
|
||||||
|
res.NodeName = peer.ComputedName
|
||||||
|
cb(res)
|
||||||
|
})
|
||||||
|
|
||||||
|
var tsmpPayload [9]byte
|
||||||
|
tsmpPayload[0] = byte(packet.TSMPTypePing)
|
||||||
|
copy(tsmpPayload[1:], data[:])
|
||||||
|
|
||||||
|
tsmpPing := packet.Generate(iph, tsmpPayload[:])
|
||||||
|
e.tundev.InjectOutbound(tsmpPing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *userspaceEngine) setTSMPPongCallback(data [8]byte, cb func()) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
if e.pongCallback == nil {
|
||||||
|
e.pongCallback = map[[8]byte]func(){}
|
||||||
|
}
|
||||||
|
if cb == nil {
|
||||||
|
delete(e.pongCallback, data)
|
||||||
|
} else {
|
||||||
|
e.pongCallback[data] = cb
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *userspaceEngine) RegisterIPPortIdentity(ipport netaddr.IPPort, tsIP netaddr.IP) {
|
func (e *userspaceEngine) RegisterIPPortIdentity(ipport netaddr.IPPort, tsIP netaddr.IP) {
|
||||||
|
@ -117,8 +117,8 @@ func (e *watchdogEngine) DiscoPublicKey() (k tailcfg.DiscoKey) {
|
|||||||
e.watchdog("DiscoPublicKey", func() { k = e.wrap.DiscoPublicKey() })
|
e.watchdog("DiscoPublicKey", func() { k = e.wrap.DiscoPublicKey() })
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
func (e *watchdogEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
func (e *watchdogEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {
|
||||||
e.watchdog("Ping", func() { e.wrap.Ping(ip, cb) })
|
e.watchdog("Ping", func() { e.wrap.Ping(ip, useTSMP, cb) })
|
||||||
}
|
}
|
||||||
func (e *watchdogEngine) RegisterIPPortIdentity(ipp netaddr.IPPort, tsIP netaddr.IP) {
|
func (e *watchdogEngine) RegisterIPPortIdentity(ipp netaddr.IPPort, tsIP netaddr.IP) {
|
||||||
e.watchdog("RegisterIPPortIdentity", func() { e.wrap.RegisterIPPortIdentity(ipp, tsIP) })
|
e.watchdog("RegisterIPPortIdentity", func() { e.wrap.RegisterIPPortIdentity(ipp, tsIP) })
|
||||||
|
@ -136,7 +136,7 @@ type Engine interface {
|
|||||||
|
|
||||||
// Ping is a request to start a discovery ping with the peer handling
|
// Ping is a request to start a discovery ping with the peer handling
|
||||||
// the given IP and then call cb with its ping latency & method.
|
// the given IP and then call cb with its ping latency & method.
|
||||||
Ping(ip netaddr.IP, cb func(*ipnstate.PingResult))
|
Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult))
|
||||||
|
|
||||||
// RegisterIPPortIdentity registers a given node (identified by its
|
// RegisterIPPortIdentity registers a given node (identified by its
|
||||||
// Tailscale IP) as temporarily having the given IP:port for whois lookups.
|
// Tailscale IP) as temporarily having the given IP:port for whois lookups.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user