Browse Source

Enable IPv6 port forwards

Rod Hynes 6 years ago
parent
commit
4c9be72337
1 changed files with 85 additions and 25 deletions
  1. 85 25
      psiphon/server/tunnelServer.go

+ 85 - 25
psiphon/server/tunnelServer.go

@@ -716,6 +716,14 @@ func (sshServer *sshServer) getLoadStats() (ProtocolStats, RegionStats) {
 		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_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
 		return stats
 	}
 
@@ -783,21 +791,45 @@ func (sshServer *sshServer) getLoadStats() (ProtocolStats, RegionStats) {
 			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_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
+				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)
+				int64(client.qualityMetrics.TCPPortForwardFailedDuration / time.Millisecond)
 			stat["tcp_port_forward_rejected_dialing_limit_count"] +=
-				client.qualityMetrics.tcpPortForwardRejectedDialingLimitCount
+				client.qualityMetrics.TCPPortForwardRejectedDialingLimitCount
+
+			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.TCPPortForwardDialedCount = 0
+		client.qualityMetrics.TCPPortForwardDialedDuration = 0
+		client.qualityMetrics.TCPPortForwardFailedCount = 0
+		client.qualityMetrics.TCPPortForwardFailedDuration = 0
+		client.qualityMetrics.TCPPortForwardRejectedDialingLimitCount = 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
 
 		client.Unlock()
 	}
@@ -1146,11 +1178,19 @@ type randomStreamMetrics struct {
 // upstream link. These stats are recorded by each sshClient
 // and then reported and reset in sshServer.getLoadStats().
 type qualityMetrics struct {
-	tcpPortForwardDialedCount               int64
-	tcpPortForwardDialedDuration            time.Duration
-	tcpPortForwardFailedCount               int64
-	tcpPortForwardFailedDuration            time.Duration
-	tcpPortForwardRejectedDialingLimitCount int64
+	TCPPortForwardDialedCount               int64
+	TCPPortForwardDialedDuration            time.Duration
+	TCPPortForwardFailedCount               int64
+	TCPPortForwardFailedDuration            time.Duration
+	TCPPortForwardRejectedDialingLimitCount int64
+	TCPIPv4PortForwardDialedCount           int64
+	TCPIPv4PortForwardDialedDuration        time.Duration
+	TCPIPv4PortForwardFailedCount           int64
+	TCPIPv4PortForwardFailedDuration        time.Duration
+	TCPIPv6PortForwardDialedCount           int64
+	TCPIPv6PortForwardDialedDuration        time.Duration
+	TCPIPv6PortForwardFailedCount           int64
+	TCPIPv6PortForwardFailedDuration        time.Duration
 }
 
 type handshakeState struct {
@@ -2941,18 +2981,31 @@ func (sshClient *sshClient) closedPortForward(
 }
 
 func (sshClient *sshClient) updateQualityMetricsWithDialResult(
-	tcpPortForwardDialSuccess bool, dialDuration time.Duration) {
+	tcpPortForwardDialSuccess bool, dialDuration time.Duration, IP net.IP) {
 
 	sshClient.Lock()
 	defer sshClient.Unlock()
 
 	if tcpPortForwardDialSuccess {
-		sshClient.qualityMetrics.tcpPortForwardDialedCount += 1
-		sshClient.qualityMetrics.tcpPortForwardDialedDuration += dialDuration
-
+		sshClient.qualityMetrics.TCPPortForwardDialedCount += 1
+		sshClient.qualityMetrics.TCPPortForwardDialedDuration += dialDuration
+		if IP.To4() != nil {
+			sshClient.qualityMetrics.TCPIPv4PortForwardDialedCount += 1
+			sshClient.qualityMetrics.TCPIPv4PortForwardDialedDuration += dialDuration
+		} else if IP != nil {
+			sshClient.qualityMetrics.TCPIPv6PortForwardDialedCount += 1
+			sshClient.qualityMetrics.TCPIPv6PortForwardDialedDuration += dialDuration
+		}
 	} else {
-		sshClient.qualityMetrics.tcpPortForwardFailedCount += 1
-		sshClient.qualityMetrics.tcpPortForwardFailedDuration += dialDuration
+		sshClient.qualityMetrics.TCPPortForwardFailedCount += 1
+		sshClient.qualityMetrics.TCPPortForwardFailedDuration += dialDuration
+		if IP.To4() != nil {
+			sshClient.qualityMetrics.TCPIPv4PortForwardFailedCount += 1
+			sshClient.qualityMetrics.TCPIPv4PortForwardFailedDuration += dialDuration
+		} else if IP != nil {
+			sshClient.qualityMetrics.TCPIPv6PortForwardFailedCount += 1
+			sshClient.qualityMetrics.TCPIPv6PortForwardFailedDuration += dialDuration
+		}
 	}
 }
 
@@ -2961,7 +3014,7 @@ func (sshClient *sshClient) updateQualityMetricsWithRejectedDialingLimit() {
 	sshClient.Lock()
 	defer sshClient.Unlock()
 
-	sshClient.qualityMetrics.tcpPortForwardRejectedDialingLimitCount += 1
+	sshClient.qualityMetrics.TCPPortForwardRejectedDialingLimitCount += 1
 }
 
 func (sshClient *sshClient) handleTCPChannel(
@@ -3015,8 +3068,10 @@ 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"
 
+	// 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?
-	// TODO: IPv6 support
+
 	var IP net.IP
 	for _, ip := range IPs {
 		if ip.IP.To4() != nil {
@@ -3024,6 +3079,11 @@ func (sshClient *sshClient) handleTCPChannel(
 			break
 		}
 	}
+	if IP == nil && len(IPs) > 0 {
+		// If there are no IPv4 IPs, the first IP is IPv6.
+		IP = IPs[0].IP
+	}
+
 	if err == nil && IP == nil {
 		err = std_errors.New("no IP address")
 	}
@@ -3033,7 +3093,7 @@ func (sshClient *sshClient) handleTCPChannel(
 	if err != nil {
 
 		// Record a port forward failure
-		sshClient.updateQualityMetricsWithDialResult(false, resolveElapsedTime)
+		sshClient.updateQualityMetricsWithDialResult(false, resolveElapsedTime, IP)
 
 		sshClient.rejectNewChannel(newChannel, fmt.Sprintf("LookupIP failed: %s", err))
 		return
@@ -3071,7 +3131,7 @@ func (sshClient *sshClient) handleTCPChannel(
 	cancelCtx() // "must be called or the new context will remain live until its parent context is cancelled"
 
 	// Record port forward success or failure
-	sshClient.updateQualityMetricsWithDialResult(err == nil, time.Since(dialStartTime))
+	sshClient.updateQualityMetricsWithDialResult(err == nil, time.Since(dialStartTime), IP)
 
 	if err != nil {