|
|
@@ -717,53 +717,87 @@ func (sshServer *sshServer) unregisterEstablishedClient(client *sshClient) {
|
|
|
client.stop()
|
|
|
}
|
|
|
|
|
|
-type ProtocolStats map[string]map[string]int64
|
|
|
-type RegionStats map[string]map[string]map[string]int64
|
|
|
+type ProtocolStats map[string]map[string]interface{}
|
|
|
+type RegionStats map[string]map[string]map[string]interface{}
|
|
|
|
|
|
func (sshServer *sshServer) getLoadStats() (ProtocolStats, RegionStats) {
|
|
|
|
|
|
sshServer.clientsMutex.Lock()
|
|
|
defer sshServer.clientsMutex.Unlock()
|
|
|
|
|
|
- // Explicitly populate with zeros to ensure 0 counts in log messages
|
|
|
- zeroStats := func() map[string]int64 {
|
|
|
- stats := make(map[string]int64)
|
|
|
- stats["accepted_clients"] = 0
|
|
|
- stats["established_clients"] = 0
|
|
|
- stats["dialing_tcp_port_forwards"] = 0
|
|
|
- stats["tcp_port_forwards"] = 0
|
|
|
- stats["total_tcp_port_forwards"] = 0
|
|
|
- stats["udp_port_forwards"] = 0
|
|
|
- stats["total_udp_port_forwards"] = 0
|
|
|
- stats["tcp_port_forward_dialed_count"] = 0
|
|
|
- stats["tcp_port_forward_dialed_duration"] = 0
|
|
|
- stats["tcp_port_forward_failed_count"] = 0
|
|
|
- stats["tcp_port_forward_failed_duration"] = 0
|
|
|
- stats["tcp_port_forward_rejected_dialing_limit_count"] = 0
|
|
|
- stats["tcp_port_forward_rejected_disallowed_count"] = 0
|
|
|
- stats["udp_port_forward_rejected_disallowed_count"] = 0
|
|
|
- stats["tcp_ipv4_port_forward_dialed_count"] = 0
|
|
|
- stats["tcp_ipv4_port_forward_dialed_duration"] = 0
|
|
|
- stats["tcp_ipv4_port_forward_failed_count"] = 0
|
|
|
- stats["tcp_ipv4_port_forward_failed_duration"] = 0
|
|
|
- stats["tcp_ipv6_port_forward_dialed_count"] = 0
|
|
|
- stats["tcp_ipv6_port_forward_dialed_duration"] = 0
|
|
|
- stats["tcp_ipv6_port_forward_failed_count"] = 0
|
|
|
- stats["tcp_ipv6_port_forward_failed_duration"] = 0
|
|
|
+ // Explicitly populate with zeros to ensure 0 counts in log messages.
|
|
|
+
|
|
|
+ zeroStats := func() map[string]interface{} {
|
|
|
+ stats := make(map[string]interface{})
|
|
|
+ stats["accepted_clients"] = int64(0)
|
|
|
+ stats["established_clients"] = int64(0)
|
|
|
+ return stats
|
|
|
+ }
|
|
|
+
|
|
|
+ // Due to hot reload and changes to the underlying system configuration, the
|
|
|
+ // set of resolver IPs may change between getLoadStats calls, so this
|
|
|
+ // enumeration for zeroing is a best effort.
|
|
|
+ resolverIPs := sshServer.support.DNSResolver.GetAll()
|
|
|
+
|
|
|
+ // Only the non-region "ALL" log has the following fields, which are
|
|
|
+ // primarily concerned with upstream/egress performance.
|
|
|
+ zeroStatsAll := func() map[string]interface{} {
|
|
|
+ stats := zeroStats()
|
|
|
+ stats["dialing_tcp_port_forwards"] = int64(0)
|
|
|
+ stats["tcp_port_forwards"] = int64(0)
|
|
|
+ stats["total_tcp_port_forwards"] = int64(0)
|
|
|
+ stats["udp_port_forwards"] = int64(0)
|
|
|
+ stats["total_udp_port_forwards"] = int64(0)
|
|
|
+ stats["tcp_port_forward_dialed_count"] = int64(0)
|
|
|
+ stats["tcp_port_forward_dialed_duration"] = int64(0)
|
|
|
+ stats["tcp_port_forward_failed_count"] = int64(0)
|
|
|
+ stats["tcp_port_forward_failed_duration"] = int64(0)
|
|
|
+ stats["tcp_port_forward_rejected_dialing_limit_count"] = int64(0)
|
|
|
+ stats["tcp_port_forward_rejected_disallowed_count"] = int64(0)
|
|
|
+ stats["udp_port_forward_rejected_disallowed_count"] = int64(0)
|
|
|
+ stats["tcp_ipv4_port_forward_dialed_count"] = int64(0)
|
|
|
+ stats["tcp_ipv4_port_forward_dialed_duration"] = int64(0)
|
|
|
+ stats["tcp_ipv4_port_forward_failed_count"] = int64(0)
|
|
|
+ stats["tcp_ipv4_port_forward_failed_duration"] = int64(0)
|
|
|
+ stats["tcp_ipv6_port_forward_dialed_count"] = int64(0)
|
|
|
+ stats["tcp_ipv6_port_forward_dialed_duration"] = int64(0)
|
|
|
+ stats["tcp_ipv6_port_forward_failed_count"] = int64(0)
|
|
|
+ stats["tcp_ipv6_port_forward_failed_duration"] = int64(0)
|
|
|
+
|
|
|
+ zeroDNSStats := func() map[string]int64 {
|
|
|
+ m := map[string]int64{"ALL": 0}
|
|
|
+ for _, resolverIP := range resolverIPs {
|
|
|
+ m[resolverIP.String()] = 0
|
|
|
+ }
|
|
|
+ return m
|
|
|
+ }
|
|
|
+
|
|
|
+ stats["dns_count"] = zeroDNSStats()
|
|
|
+ stats["dns_duration"] = zeroDNSStats()
|
|
|
+ stats["dns_failed_count"] = zeroDNSStats()
|
|
|
+ stats["dns_failed_duration"] = zeroDNSStats()
|
|
|
return stats
|
|
|
}
|
|
|
|
|
|
- zeroProtocolStats := func() map[string]map[string]int64 {
|
|
|
- stats := make(map[string]map[string]int64)
|
|
|
- stats["ALL"] = zeroStats()
|
|
|
+ zeroProtocolStats := func(nonRegion bool) map[string]map[string]interface{} {
|
|
|
+ stats := make(map[string]map[string]interface{})
|
|
|
+ if nonRegion {
|
|
|
+ stats["ALL"] = zeroStatsAll()
|
|
|
+ } else {
|
|
|
+ stats["ALL"] = zeroStats()
|
|
|
+ }
|
|
|
for tunnelProtocol := range sshServer.support.Config.TunnelProtocolPorts {
|
|
|
stats[tunnelProtocol] = zeroStats()
|
|
|
}
|
|
|
return stats
|
|
|
}
|
|
|
|
|
|
+ addInt64 := func(stats map[string]interface{}, name string, value int64) {
|
|
|
+ stats[name] = stats[name].(int64) + value
|
|
|
+ }
|
|
|
+
|
|
|
// [<protocol or ALL>][<stat name>] -> count
|
|
|
- protocolStats := zeroProtocolStats()
|
|
|
+ protocolStats := zeroProtocolStats(true)
|
|
|
|
|
|
// [<region][<protocol or ALL>][<stat name>] -> count
|
|
|
regionStats := make(RegionStats)
|
|
|
@@ -775,14 +809,14 @@ func (sshServer *sshServer) getLoadStats() (ProtocolStats, RegionStats) {
|
|
|
|
|
|
if acceptedClientCount > 0 {
|
|
|
if regionStats[region] == nil {
|
|
|
- regionStats[region] = zeroProtocolStats()
|
|
|
+ regionStats[region] = zeroProtocolStats(false)
|
|
|
}
|
|
|
|
|
|
- protocolStats["ALL"]["accepted_clients"] += acceptedClientCount
|
|
|
- protocolStats[tunnelProtocol]["accepted_clients"] += acceptedClientCount
|
|
|
+ addInt64(protocolStats["ALL"], "accepted_clients", acceptedClientCount)
|
|
|
+ addInt64(protocolStats[tunnelProtocol], "accepted_clients", acceptedClientCount)
|
|
|
|
|
|
- regionStats[region]["ALL"]["accepted_clients"] += acceptedClientCount
|
|
|
- regionStats[region][tunnelProtocol]["accepted_clients"] += acceptedClientCount
|
|
|
+ addInt64(regionStats[region]["ALL"], "accepted_clients", acceptedClientCount)
|
|
|
+ addInt64(regionStats[region][tunnelProtocol], "accepted_clients", acceptedClientCount)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -795,73 +829,96 @@ func (sshServer *sshServer) getLoadStats() (ProtocolStats, RegionStats) {
|
|
|
region := client.geoIPData.Country
|
|
|
|
|
|
if regionStats[region] == nil {
|
|
|
- regionStats[region] = zeroProtocolStats()
|
|
|
+ regionStats[region] = zeroProtocolStats(false)
|
|
|
}
|
|
|
|
|
|
- stats := []map[string]int64{
|
|
|
+ for _, stats := range []map[string]interface{}{
|
|
|
protocolStats["ALL"],
|
|
|
protocolStats[tunnelProtocol],
|
|
|
regionStats[region]["ALL"],
|
|
|
- regionStats[region][tunnelProtocol]}
|
|
|
-
|
|
|
- for _, stat := range stats {
|
|
|
-
|
|
|
- stat["established_clients"] += 1
|
|
|
-
|
|
|
- // Note: can't sum trafficState.peakConcurrentPortForwardCount to get a global peak
|
|
|
-
|
|
|
- stat["dialing_tcp_port_forwards"] += client.tcpTrafficState.concurrentDialingPortForwardCount
|
|
|
- stat["tcp_port_forwards"] += client.tcpTrafficState.concurrentPortForwardCount
|
|
|
- stat["total_tcp_port_forwards"] += client.tcpTrafficState.totalPortForwardCount
|
|
|
- // client.udpTrafficState.concurrentDialingPortForwardCount isn't meaningful
|
|
|
- stat["udp_port_forwards"] += client.udpTrafficState.concurrentPortForwardCount
|
|
|
- stat["total_udp_port_forwards"] += client.udpTrafficState.totalPortForwardCount
|
|
|
-
|
|
|
- stat["tcp_port_forward_dialed_count"] += client.qualityMetrics.TCPPortForwardDialedCount
|
|
|
- stat["tcp_port_forward_dialed_duration"] +=
|
|
|
- int64(client.qualityMetrics.TCPPortForwardDialedDuration / time.Millisecond)
|
|
|
- stat["tcp_port_forward_failed_count"] += client.qualityMetrics.TCPPortForwardFailedCount
|
|
|
- stat["tcp_port_forward_failed_duration"] +=
|
|
|
- int64(client.qualityMetrics.TCPPortForwardFailedDuration / time.Millisecond)
|
|
|
- stat["tcp_port_forward_rejected_dialing_limit_count"] +=
|
|
|
- client.qualityMetrics.TCPPortForwardRejectedDialingLimitCount
|
|
|
- stat["tcp_port_forward_rejected_disallowed_count"] +=
|
|
|
- client.qualityMetrics.TCPPortForwardRejectedDisallowedCount
|
|
|
- stat["udp_port_forward_rejected_disallowed_count"] +=
|
|
|
- client.qualityMetrics.UDPPortForwardRejectedDisallowedCount
|
|
|
-
|
|
|
- stat["tcp_ipv4_port_forward_dialed_count"] += client.qualityMetrics.TCPIPv4PortForwardDialedCount
|
|
|
- stat["tcp_ipv4_port_forward_dialed_duration"] +=
|
|
|
- int64(client.qualityMetrics.TCPIPv4PortForwardDialedDuration / time.Millisecond)
|
|
|
- stat["tcp_ipv4_port_forward_failed_count"] += client.qualityMetrics.TCPIPv4PortForwardFailedCount
|
|
|
- stat["tcp_ipv4_port_forward_failed_duration"] +=
|
|
|
- int64(client.qualityMetrics.TCPIPv4PortForwardFailedDuration / time.Millisecond)
|
|
|
-
|
|
|
- stat["tcp_ipv6_port_forward_dialed_count"] += client.qualityMetrics.TCPIPv6PortForwardDialedCount
|
|
|
- stat["tcp_ipv6_port_forward_dialed_duration"] +=
|
|
|
- int64(client.qualityMetrics.TCPIPv6PortForwardDialedDuration / time.Millisecond)
|
|
|
- stat["tcp_ipv6_port_forward_failed_count"] += client.qualityMetrics.TCPIPv6PortForwardFailedCount
|
|
|
- stat["tcp_ipv6_port_forward_failed_duration"] +=
|
|
|
- int64(client.qualityMetrics.TCPIPv6PortForwardFailedDuration / time.Millisecond)
|
|
|
- }
|
|
|
-
|
|
|
- client.qualityMetrics.TCPPortForwardDialedCount = 0
|
|
|
- client.qualityMetrics.TCPPortForwardDialedDuration = 0
|
|
|
- client.qualityMetrics.TCPPortForwardFailedCount = 0
|
|
|
- client.qualityMetrics.TCPPortForwardFailedDuration = 0
|
|
|
- client.qualityMetrics.TCPPortForwardRejectedDialingLimitCount = 0
|
|
|
- client.qualityMetrics.TCPPortForwardRejectedDisallowedCount = 0
|
|
|
- client.qualityMetrics.UDPPortForwardRejectedDisallowedCount = 0
|
|
|
-
|
|
|
- client.qualityMetrics.TCPIPv4PortForwardDialedCount = 0
|
|
|
- client.qualityMetrics.TCPIPv4PortForwardDialedDuration = 0
|
|
|
- client.qualityMetrics.TCPIPv4PortForwardFailedCount = 0
|
|
|
- client.qualityMetrics.TCPIPv4PortForwardFailedDuration = 0
|
|
|
-
|
|
|
- client.qualityMetrics.TCPIPv6PortForwardDialedCount = 0
|
|
|
- client.qualityMetrics.TCPIPv6PortForwardDialedDuration = 0
|
|
|
- client.qualityMetrics.TCPIPv6PortForwardFailedCount = 0
|
|
|
- client.qualityMetrics.TCPIPv6PortForwardFailedDuration = 0
|
|
|
+ regionStats[region][tunnelProtocol]} {
|
|
|
+
|
|
|
+ addInt64(stats, "established_clients", 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ stats := protocolStats["ALL"]
|
|
|
+
|
|
|
+ // Note:
|
|
|
+ // - can't sum trafficState.peakConcurrentPortForwardCount to get a global peak
|
|
|
+ // - client.udpTrafficState.concurrentDialingPortForwardCount isn't meaningful
|
|
|
+
|
|
|
+ addInt64(stats, "dialing_tcp_port_forwards", client.tcpTrafficState.concurrentDialingPortForwardCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forwards", client.tcpTrafficState.concurrentPortForwardCount)
|
|
|
+
|
|
|
+ addInt64(stats, "total_tcp_port_forwards", client.tcpTrafficState.totalPortForwardCount)
|
|
|
+
|
|
|
+ addInt64(stats, "udp_port_forwards", client.udpTrafficState.concurrentPortForwardCount)
|
|
|
+
|
|
|
+ addInt64(stats, "total_udp_port_forwards", client.udpTrafficState.totalPortForwardCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forward_dialed_count", client.qualityMetrics.TCPPortForwardDialedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forward_dialed_duration",
|
|
|
+ int64(client.qualityMetrics.TCPPortForwardDialedDuration/time.Millisecond))
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forward_failed_count", client.qualityMetrics.TCPPortForwardFailedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forward_failed_duration",
|
|
|
+ int64(client.qualityMetrics.TCPPortForwardFailedDuration/time.Millisecond))
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forward_rejected_dialing_limit_count",
|
|
|
+ client.qualityMetrics.TCPPortForwardRejectedDialingLimitCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_port_forward_rejected_disallowed_count",
|
|
|
+ client.qualityMetrics.TCPPortForwardRejectedDisallowedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "udp_port_forward_rejected_disallowed_count",
|
|
|
+ client.qualityMetrics.UDPPortForwardRejectedDisallowedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv4_port_forward_dialed_count", client.qualityMetrics.TCPIPv4PortForwardDialedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv4_port_forward_dialed_duration",
|
|
|
+ int64(client.qualityMetrics.TCPIPv4PortForwardDialedDuration/time.Millisecond))
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv4_port_forward_failed_count", client.qualityMetrics.TCPIPv4PortForwardFailedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv4_port_forward_failed_duration",
|
|
|
+ int64(client.qualityMetrics.TCPIPv4PortForwardFailedDuration/time.Millisecond))
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv6_port_forward_dialed_count", client.qualityMetrics.TCPIPv6PortForwardDialedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv6_port_forward_dialed_duration",
|
|
|
+ int64(client.qualityMetrics.TCPIPv6PortForwardDialedDuration/time.Millisecond))
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv6_port_forward_failed_count", client.qualityMetrics.TCPIPv6PortForwardFailedCount)
|
|
|
+
|
|
|
+ addInt64(stats, "tcp_ipv6_port_forward_failed_duration",
|
|
|
+ int64(client.qualityMetrics.TCPIPv6PortForwardFailedDuration/time.Millisecond))
|
|
|
+
|
|
|
+ // DNS metrics limitations:
|
|
|
+ // - port forwards (sshClient.handleTCPChannel) don't know or log the resolver IP.
|
|
|
+ // - udpgw and packet tunnel transparent DNS use a heuristic to classify success/failure.
|
|
|
+
|
|
|
+ // Every client.qualityMetrics DNS map has an "ALL" entry.
|
|
|
+
|
|
|
+ for key, value := range client.qualityMetrics.DNSCount {
|
|
|
+ stats["dns_count"].(map[string]int64)[key] += value
|
|
|
+ }
|
|
|
+
|
|
|
+ for key, value := range client.qualityMetrics.DNSDuration {
|
|
|
+ stats["dns_duration"].(map[string]int64)[key] += int64(value / time.Millisecond)
|
|
|
+ }
|
|
|
+
|
|
|
+ for key, value := range client.qualityMetrics.DNSFailedCount {
|
|
|
+ stats["dns_failed_count"].(map[string]int64)[key] += value
|
|
|
+ }
|
|
|
+
|
|
|
+ for key, value := range client.qualityMetrics.DNSFailedDuration {
|
|
|
+ stats["dns_failed_duration"].(map[string]int64)[key] += int64(value / time.Millisecond)
|
|
|
+ }
|
|
|
+
|
|
|
+ client.qualityMetrics.reset()
|
|
|
|
|
|
client.Unlock()
|
|
|
}
|
|
|
@@ -1215,7 +1272,7 @@ type sshClient struct {
|
|
|
trafficRules TrafficRules
|
|
|
tcpTrafficState trafficState
|
|
|
udpTrafficState trafficState
|
|
|
- qualityMetrics qualityMetrics
|
|
|
+ qualityMetrics *qualityMetrics
|
|
|
tcpPortForwardLRU *common.LRUConns
|
|
|
oslClientSeedState *osl.ClientSeedState
|
|
|
signalIssueSLOKs chan struct{}
|
|
|
@@ -1271,6 +1328,57 @@ type qualityMetrics struct {
|
|
|
TCPIPv6PortForwardDialedDuration time.Duration
|
|
|
TCPIPv6PortForwardFailedCount int64
|
|
|
TCPIPv6PortForwardFailedDuration time.Duration
|
|
|
+ DNSCount map[string]int64
|
|
|
+ DNSDuration map[string]time.Duration
|
|
|
+ DNSFailedCount map[string]int64
|
|
|
+ DNSFailedDuration map[string]time.Duration
|
|
|
+}
|
|
|
+
|
|
|
+func newQualityMetrics() *qualityMetrics {
|
|
|
+ return &qualityMetrics{
|
|
|
+ DNSCount: make(map[string]int64),
|
|
|
+ DNSDuration: make(map[string]time.Duration),
|
|
|
+ DNSFailedCount: make(map[string]int64),
|
|
|
+ DNSFailedDuration: make(map[string]time.Duration),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (q *qualityMetrics) reset() {
|
|
|
+
|
|
|
+ q.TCPPortForwardDialedCount = 0
|
|
|
+ q.TCPPortForwardDialedDuration = 0
|
|
|
+ q.TCPPortForwardFailedCount = 0
|
|
|
+ q.TCPPortForwardFailedDuration = 0
|
|
|
+ q.TCPPortForwardRejectedDialingLimitCount = 0
|
|
|
+ q.TCPPortForwardRejectedDisallowedCount = 0
|
|
|
+
|
|
|
+ q.UDPPortForwardRejectedDisallowedCount = 0
|
|
|
+
|
|
|
+ q.TCPIPv4PortForwardDialedCount = 0
|
|
|
+ q.TCPIPv4PortForwardDialedDuration = 0
|
|
|
+ q.TCPIPv4PortForwardFailedCount = 0
|
|
|
+ q.TCPIPv4PortForwardFailedDuration = 0
|
|
|
+
|
|
|
+ q.TCPIPv6PortForwardDialedCount = 0
|
|
|
+ q.TCPIPv6PortForwardDialedDuration = 0
|
|
|
+ q.TCPIPv6PortForwardFailedCount = 0
|
|
|
+ q.TCPIPv6PortForwardFailedDuration = 0
|
|
|
+
|
|
|
+ // Retain existing maps to avoid memory churn. The Go compiler optimizes map
|
|
|
+ // clearing operations of the following form.
|
|
|
+
|
|
|
+ for k := range q.DNSCount {
|
|
|
+ delete(q.DNSCount, k)
|
|
|
+ }
|
|
|
+ for k := range q.DNSDuration {
|
|
|
+ delete(q.DNSDuration, k)
|
|
|
+ }
|
|
|
+ for k := range q.DNSFailedCount {
|
|
|
+ delete(q.DNSFailedCount, k)
|
|
|
+ }
|
|
|
+ for k := range q.DNSFailedDuration {
|
|
|
+ delete(q.DNSFailedDuration, k)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
type handshakeState struct {
|
|
|
@@ -1316,6 +1424,7 @@ func newSshClient(
|
|
|
clientAddr: clientAddr,
|
|
|
geoIPData: geoIPData,
|
|
|
isFirstTunnelInSession: true,
|
|
|
+ qualityMetrics: newQualityMetrics(),
|
|
|
tcpPortForwardLRU: common.NewLRUConns(),
|
|
|
signalIssueSLOKs: make(chan struct{}, 1),
|
|
|
runCtx: runCtx,
|
|
|
@@ -2347,6 +2456,8 @@ func (sshClient *sshClient) handleNewPacketTunnelChannel(
|
|
|
sshClient.Unlock()
|
|
|
}
|
|
|
|
|
|
+ dnsQualityReporter := sshClient.updateQualityMetricsWithDNSResult
|
|
|
+
|
|
|
err = sshClient.sshServer.support.PacketTunnelServer.ClientConnected(
|
|
|
sshClient.sessionID,
|
|
|
packetTunnelChannel,
|
|
|
@@ -2354,7 +2465,8 @@ func (sshClient *sshClient) handleNewPacketTunnelChannel(
|
|
|
checkAllowedUDPPortFunc,
|
|
|
checkAllowedDomainFunc,
|
|
|
flowActivityUpdaterMaker,
|
|
|
- metricUpdater)
|
|
|
+ metricUpdater,
|
|
|
+ dnsQualityReporter)
|
|
|
if err != nil {
|
|
|
log.WithTraceFields(LogFields{"error": err}).Warning("start packet tunnel client failed")
|
|
|
sshClient.setPacketTunnelChannel(nil)
|
|
|
@@ -3506,6 +3618,33 @@ func (sshClient *sshClient) updateQualityMetricsWithUDPRejectedDisallowed() {
|
|
|
sshClient.qualityMetrics.UDPPortForwardRejectedDisallowedCount += 1
|
|
|
}
|
|
|
|
|
|
+func (sshClient *sshClient) updateQualityMetricsWithDNSResult(
|
|
|
+ success bool, duration time.Duration, resolverIP net.IP) {
|
|
|
+
|
|
|
+ sshClient.Lock()
|
|
|
+ defer sshClient.Unlock()
|
|
|
+
|
|
|
+ resolver := ""
|
|
|
+ if resolverIP != nil {
|
|
|
+ resolver = resolverIP.String()
|
|
|
+ }
|
|
|
+ if success {
|
|
|
+ sshClient.qualityMetrics.DNSCount["ALL"] += 1
|
|
|
+ sshClient.qualityMetrics.DNSDuration["ALL"] += duration
|
|
|
+ if resolver != "" {
|
|
|
+ sshClient.qualityMetrics.DNSCount[resolver] += 1
|
|
|
+ sshClient.qualityMetrics.DNSDuration[resolver] += duration
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ sshClient.qualityMetrics.DNSFailedCount["ALL"] += 1
|
|
|
+ sshClient.qualityMetrics.DNSFailedDuration["ALL"] += duration
|
|
|
+ if resolver != "" {
|
|
|
+ sshClient.qualityMetrics.DNSFailedCount[resolver] += 1
|
|
|
+ sshClient.qualityMetrics.DNSFailedDuration[resolver] += duration
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func (sshClient *sshClient) handleTCPChannel(
|
|
|
remainingDialTimeout time.Duration,
|
|
|
hostToConnect string,
|
|
|
@@ -3583,6 +3722,17 @@ func (sshClient *sshClient) handleTCPChannel(
|
|
|
IPs, err := (&net.Resolver{}).LookupIPAddr(ctx, hostToConnect)
|
|
|
cancelCtx() // "must be called or the new context will remain live until its parent context is cancelled"
|
|
|
|
|
|
+ resolveElapsedTime := time.Since(dialStartTime)
|
|
|
+
|
|
|
+ // Record DNS metrics. If LookupIPAddr returns net.DNSError.IsNotFound, this
|
|
|
+ // is "no such host" and not a DNS failure. Limitation: the resolver IP is
|
|
|
+ // not known.
|
|
|
+
|
|
|
+ dnsErr, ok := err.(*net.DNSError)
|
|
|
+ dnsNotFound := ok && dnsErr.IsNotFound
|
|
|
+ dnsSuccess := err == nil || dnsNotFound
|
|
|
+ sshClient.updateQualityMetricsWithDNSResult(dnsSuccess, resolveElapsedTime, nil)
|
|
|
+
|
|
|
// IPv4 is preferred in case the host has limited IPv6 routing. IPv6 is
|
|
|
// selected and attempted only when there's no IPv4 option.
|
|
|
// TODO: shuffle list to try other IPs?
|
|
|
@@ -3603,8 +3753,6 @@ func (sshClient *sshClient) handleTCPChannel(
|
|
|
err = std_errors.New("no IP address")
|
|
|
}
|
|
|
|
|
|
- resolveElapsedTime := time.Since(dialStartTime)
|
|
|
-
|
|
|
if err != nil {
|
|
|
|
|
|
// Record a port forward failure
|