|
|
|
|
@ -45,6 +45,7 @@ import ( |
|
|
|
|
"tailscale.com/types/netmap" |
|
|
|
|
"tailscale.com/util/clientmetric" |
|
|
|
|
"tailscale.com/util/deephash" |
|
|
|
|
"tailscale.com/util/mak" |
|
|
|
|
"tailscale.com/version" |
|
|
|
|
"tailscale.com/wgengine/filter" |
|
|
|
|
"tailscale.com/wgengine/magicsock" |
|
|
|
|
@ -135,8 +136,15 @@ type userspaceEngine struct { |
|
|
|
|
endpoints []tailcfg.Endpoint |
|
|
|
|
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
|
|
|
|
|
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
|
|
|
|
|
pongCallback map[[8]byte]func(packet.TSMPPongReply) // for TSMP pong responses
|
|
|
|
|
tsIPByIPPort map[netaddr.IPPort]netaddr.IP // allows registration of IP:ports as belonging to a certain Tailscale IP for whois lookups
|
|
|
|
|
|
|
|
|
|
// pongCallback is the map of response handlers waiting for disco or TSMP
|
|
|
|
|
// pong callbacks. The map key is a random slice of bytes.
|
|
|
|
|
pongCallback map[[8]byte]func(packet.TSMPPongReply) |
|
|
|
|
// icmpEchoResponseCallback is the map of reponse handlers waiting for ICMP
|
|
|
|
|
// echo responses. The map key is a random uint32 that is the little endian
|
|
|
|
|
// value of the ICMP identifer and sequence number concatenated.
|
|
|
|
|
icmpEchoResponseCallback map[uint32]func() |
|
|
|
|
|
|
|
|
|
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
|
|
|
|
} |
|
|
|
|
@ -387,6 +395,20 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
e.tundev.OnICMPEchoResponseReceived = func(p *packet.Parsed) bool { |
|
|
|
|
idSeq := p.EchoIDSeq() |
|
|
|
|
e.mu.Lock() |
|
|
|
|
defer e.mu.Unlock() |
|
|
|
|
cb := e.icmpEchoResponseCallback[idSeq] |
|
|
|
|
if cb == nil { |
|
|
|
|
// We didn't swallow it, so let it flow to the host.
|
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
e.logf("wgengine: got diagnostic ICMP response %02x", idSeq) |
|
|
|
|
go cb() |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// wgdev takes ownership of tundev, will close it when closed.
|
|
|
|
|
e.logf("Creating wireguard device...") |
|
|
|
|
e.wgdev = wgcfg.NewDevice(e.tundev, e.magicConn.Bind(), e.wgLogger.DeviceLogger) |
|
|
|
|
@ -1267,7 +1289,7 @@ func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) { |
|
|
|
|
e.magicConn.UpdateStatus(sb) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) { |
|
|
|
|
func (e *userspaceEngine) Ping(ip netaddr.IP, pingType tailcfg.PingType, cb func(*ipnstate.PingResult)) { |
|
|
|
|
res := &ipnstate.PingResult{IP: ip.String()} |
|
|
|
|
pip, ok := e.PeerForIP(ip) |
|
|
|
|
if !ok { |
|
|
|
|
@ -1284,15 +1306,14 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.Pi |
|
|
|
|
} |
|
|
|
|
peer := pip.Node |
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
switch pingType { |
|
|
|
|
case "disco": |
|
|
|
|
e.magicConn.Ping(peer, res, cb) |
|
|
|
|
case "TSMP": |
|
|
|
|
e.sendTSMPPing(ip, peer, res, cb) |
|
|
|
|
case "ICMP": |
|
|
|
|
e.sendICMPEchoRequest(ip, peer, res, cb) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1313,6 +1334,55 @@ func (e *userspaceEngine) mySelfIPMatchingFamily(dst netaddr.IP) (src netaddr.IP |
|
|
|
|
return netaddr.IP{}, errors.New("no self address in netmap matching address family") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (e *userspaceEngine) sendICMPEchoRequest(destIP netaddr.IP, peer *tailcfg.Node, res *ipnstate.PingResult, cb func(*ipnstate.PingResult)) { |
|
|
|
|
srcIP, err := e.mySelfIPMatchingFamily(destIP) |
|
|
|
|
if err != nil { |
|
|
|
|
res.Err = err.Error() |
|
|
|
|
cb(res) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
var icmph packet.Header |
|
|
|
|
if srcIP.Is4() { |
|
|
|
|
icmph = packet.ICMP4Header{ |
|
|
|
|
IP4Header: packet.IP4Header{ |
|
|
|
|
IPProto: ipproto.ICMPv4, |
|
|
|
|
Src: srcIP, |
|
|
|
|
Dst: destIP, |
|
|
|
|
}, |
|
|
|
|
Type: packet.ICMP4EchoRequest, |
|
|
|
|
Code: packet.ICMP4NoCode, |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
icmph = packet.ICMP6Header{ |
|
|
|
|
IP6Header: packet.IP6Header{ |
|
|
|
|
IPProto: ipproto.ICMPv6, |
|
|
|
|
Src: srcIP, |
|
|
|
|
Dst: destIP, |
|
|
|
|
}, |
|
|
|
|
Type: packet.ICMP6EchoRequest, |
|
|
|
|
Code: packet.ICMP6NoCode, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
idSeq, payload := packet.ICMPEchoPayload(nil) |
|
|
|
|
|
|
|
|
|
expireTimer := time.AfterFunc(10*time.Second, func() { |
|
|
|
|
e.setICMPEchoResponseCallback(idSeq, nil) |
|
|
|
|
}) |
|
|
|
|
t0 := time.Now() |
|
|
|
|
e.setICMPEchoResponseCallback(idSeq, func() { |
|
|
|
|
expireTimer.Stop() |
|
|
|
|
d := time.Since(t0) |
|
|
|
|
res.LatencySeconds = d.Seconds() |
|
|
|
|
res.NodeIP = destIP.String() |
|
|
|
|
res.NodeName = peer.ComputedName |
|
|
|
|
cb(res) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
icmpPing := packet.Generate(icmph, payload) |
|
|
|
|
e.tundev.InjectOutbound(icmpPing) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
@ -1373,6 +1443,16 @@ func (e *userspaceEngine) setTSMPPongCallback(data [8]byte, cb func(packet.TSMPP |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (e *userspaceEngine) setICMPEchoResponseCallback(idSeq uint32, cb func()) { |
|
|
|
|
e.mu.Lock() |
|
|
|
|
defer e.mu.Unlock() |
|
|
|
|
if cb == nil { |
|
|
|
|
delete(e.icmpEchoResponseCallback, idSeq) |
|
|
|
|
} else { |
|
|
|
|
mak.Set(&e.icmpEchoResponseCallback, idSeq, cb) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (e *userspaceEngine) RegisterIPPortIdentity(ipport netaddr.IPPort, tsIP netaddr.IP) { |
|
|
|
|
e.mu.Lock() |
|
|
|
|
defer e.mu.Unlock() |
|
|
|
|
|