Browse Source

Custom network latency multiplier modifications

- Rename: remove "Custom" qualifier from
  parameter names where unnecessary.

- Fix: MakeDialParameters reselects network latency
  multiplier if replay value is outside of min/max.

- To support platform clients that set NetworkLatencyMultiplier
  via config, the min/max range is set to this value
  when specified.

- For testing, min/max can be specified via config.
Rod Hynes 6 years ago
parent
commit
24a03862a1

+ 7 - 7
psiphon/common/parameters/clientParameters.go

@@ -68,9 +68,9 @@ import (
 
 const (
 	NetworkLatencyMultiplier                         = "NetworkLatencyMultiplier"
-	CustomNetworkLatencyMultiplierMin                = "CustomNetworkLatencyMultiplierMin"
-	CustomNetworkLatencyMultiplierMax                = "CustomNetworkLatencyMultiplierMax"
-	CustomNetworkLatencyMultiplierLambda             = "CustomNetworkLatencyMultiplierLambda"
+	NetworkLatencyMultiplierMin                      = "NetworkLatencyMultiplierMin"
+	NetworkLatencyMultiplierMax                      = "NetworkLatencyMultiplierMax"
+	NetworkLatencyMultiplierLambda                   = "NetworkLatencyMultiplierLambda"
 	TacticsWaitPeriod                                = "TacticsWaitPeriod"
 	TacticsRetryPeriod                               = "TacticsRetryPeriod"
 	TacticsRetryPeriodJitter                         = "TacticsRetryPeriodJitter"
@@ -246,10 +246,10 @@ var defaultClientParameters = map[string]struct {
 	// NetworkLatencyMultiplier defaults to 0, meaning off. But when set, it
 	// must be a multiplier >= 1.
 
-	NetworkLatencyMultiplier:             {value: 0.0, minimum: 1.0},
-	CustomNetworkLatencyMultiplierMin:    {value: 1.0, minimum: 1.0},
-	CustomNetworkLatencyMultiplierMax:    {value: 3.0, minimum: 1.0},
-	CustomNetworkLatencyMultiplierLambda: {value: 2.0, minimum: 1.0},
+	NetworkLatencyMultiplier:       {value: 0.0, minimum: 1.0},
+	NetworkLatencyMultiplierMin:    {value: 1.0, minimum: 1.0},
+	NetworkLatencyMultiplierMax:    {value: 3.0, minimum: 1.0},
+	NetworkLatencyMultiplierLambda: {value: 2.0, minimum: 1.0},
 
 	TacticsWaitPeriod:        {value: 10 * time.Second, minimum: 0 * time.Second, flags: useNetworkLatencyMultiplier},
 	TacticsRetryPeriod:       {value: 5 * time.Second, minimum: 1 * time.Millisecond},

+ 26 - 8
psiphon/config.go

@@ -541,8 +541,7 @@ type Config struct {
 	LivenessTestMinDownstreamBytes *int
 	LivenessTestMaxDownstreamBytes *int
 
-	// ReplayCandidateCount and other Replay fields are for
-	// testing purposes.
+	// ReplayCandidateCount and other Replay fields are for testing purposes.
 	ReplayCandidateCount                   *int
 	ReplayDialParametersTTLSeconds         *int
 	ReplayTargetUpstreamBytes              *int
@@ -551,6 +550,11 @@ type Config struct {
 	ReplayLaterRoundMoveToFrontProbability *float64
 	ReplayRetainFailedProbability          *float64
 
+	// NetworkLatencyMultiplierMin and NetworkLatencyMultiplierMax are for
+	// testing purposes.
+	NetworkLatencyMultiplierMin float64
+	NetworkLatencyMultiplierMax float64
+
 	// UseOnlyCustomTLSProfiles and other TLS configuration fields are for
 	// testing purposes.
 	UseOnlyCustomTLSProfiles              *bool
@@ -843,12 +847,11 @@ func (config *Config) SetClientParameters(tag string, skipOnError bool, applyPar
 	NoticeInfo("applied %v parameters with tag '%s'", counts, tag)
 
 	// Emit certain individual parameter values for quick reference in diagnostics.
-	networkLatencyMultiplier := config.clientParameters.Get().Float(parameters.NetworkLatencyMultiplier)
-	if networkLatencyMultiplier != 0.0 {
-		NoticeInfo(
-			"NetworkLatencyMultiplier: %f",
-			config.clientParameters.Get().Float(parameters.NetworkLatencyMultiplier))
-	}
+	p := config.clientParameters.Get()
+	NoticeInfo(
+		"NetworkLatencyMultiplier Min/Max: %f/%f",
+		p.Float(parameters.NetworkLatencyMultiplierMin),
+		p.Float(parameters.NetworkLatencyMultiplierMax))
 
 	return nil
 }
@@ -904,8 +907,23 @@ func (config *Config) makeConfigParameters() map[string]interface{} {
 
 	applyParameters := make(map[string]interface{})
 
+	// To support platform clients that configure NetworkLatencyMultiplier, set
+	// the NetworkLatencyMultiplierMin/NetworkLatencyMultiplierMax range to the
+	// specified value. Also set the older NetworkLatencyMultiplier tactic, since
+	// that will be used in the case of replaying with dial parameters persisted
+	// by an older client version.
 	if config.NetworkLatencyMultiplier > 0.0 {
 		applyParameters[parameters.NetworkLatencyMultiplier] = config.NetworkLatencyMultiplier
+		applyParameters[parameters.NetworkLatencyMultiplierMin] = config.NetworkLatencyMultiplier
+		applyParameters[parameters.NetworkLatencyMultiplierMax] = config.NetworkLatencyMultiplier
+	}
+
+	if config.NetworkLatencyMultiplierMin > 0.0 {
+		applyParameters[parameters.NetworkLatencyMultiplierMin] = config.NetworkLatencyMultiplierMin
+	}
+
+	if config.NetworkLatencyMultiplierMax > 0.0 {
+		applyParameters[parameters.NetworkLatencyMultiplierMax] = config.NetworkLatencyMultiplierMax
 	}
 
 	if len(config.LimitTunnelProtocols) > 0 {

+ 41 - 29
psiphon/dialParameters.go

@@ -66,7 +66,7 @@ type DialParameters struct {
 	LastUsedTimestamp       time.Time
 	LastUsedConfigStateHash []byte
 
-	CustomNetworkLatencyMultiplier float64
+	NetworkLatencyMultiplier float64
 
 	TunnelProtocol string
 
@@ -272,22 +272,34 @@ func MakeDialParameters(
 	// replaying, existing parameters are retaing, subject to the replay-X
 	// tactics flags.
 
-	// Select a random, custom network latency multiplier. This allows clients to
+	// Select a network latency multiplier for this dial. This allows clients to
 	// explore and discover timeout values appropriate for the current network.
 	// The selection applies per tunnel, to avoid delaying all establishment
 	// candidates due to excessive timeouts. The random selection is bounded by a
 	// min/max set in tactics and an exponential distribution is used so as to
 	// heavily favor values closed to the min, which should be set to the
-	// traditional NetworkLatencyMultiplier value.
+	// singleton NetworkLatencyMultiplier tactics value.
 	//
-	// Not all existing, persisted DialParameters will have a
-	// CustomNetworkLatencyMultiplier value. Its zero value will cause the
-	// standard NetworkLatencyMultiplier to be used instead.
-	if !isReplay {
-		dialParams.CustomNetworkLatencyMultiplier = prng.ExpFloat64Range(
-			p.Float(parameters.CustomNetworkLatencyMultiplierMin),
-			p.Float(parameters.CustomNetworkLatencyMultiplierMax),
-			p.Float(parameters.CustomNetworkLatencyMultiplierLambda))
+	// Not all existing, persisted DialParameters will have a custom
+	// NetworkLatencyMultiplier value. Its zero value will cause the singleton
+	// NetworkLatencyMultiplier tactics value to be used instead, which is
+	// consistent with the pre-custom multiplier behavior in the older client
+	// version which persisted that DialParameters.
+
+	networkLatencyMultiplierMin := p.Float(parameters.NetworkLatencyMultiplierMin)
+	networkLatencyMultiplierMax := p.Float(parameters.NetworkLatencyMultiplierMax)
+
+	if !isReplay ||
+		// Was selected...
+		(dialParams.NetworkLatencyMultiplier != 0.0 &&
+			//  But is now outside tactics range...
+			(dialParams.NetworkLatencyMultiplier < networkLatencyMultiplierMin ||
+				dialParams.NetworkLatencyMultiplier > networkLatencyMultiplierMax)) {
+
+		dialParams.NetworkLatencyMultiplier = prng.ExpFloat64Range(
+			networkLatencyMultiplierMin,
+			networkLatencyMultiplierMax,
+			p.Float(parameters.NetworkLatencyMultiplierLambda))
 	}
 
 	if !isReplay && !isExchanged {
@@ -604,24 +616,24 @@ func MakeDialParameters(
 	if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) {
 
 		dialParams.meekConfig = &MeekConfig{
-			DiagnosticID:                   serverEntry.GetDiagnosticID(),
-			ClientParameters:               config.clientParameters,
-			DialAddress:                    dialParams.MeekDialAddress,
-			UseQUIC:                        protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol),
-			QUICVersion:                    dialParams.QUICVersion,
-			UseHTTPS:                       protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol),
-			TLSProfile:                     dialParams.TLSProfile,
-			NoDefaultTLSSessionID:          dialParams.NoDefaultTLSSessionID,
-			RandomizedTLSProfileSeed:       dialParams.RandomizedTLSProfileSeed,
-			UseObfuscatedSessionTickets:    dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
-			SNIServerName:                  dialParams.MeekSNIServerName,
-			HostHeader:                     dialParams.MeekHostHeader,
-			TransformedHostName:            dialParams.MeekTransformedHostName,
-			ClientTunnelProtocol:           dialParams.TunnelProtocol,
-			MeekCookieEncryptionPublicKey:  serverEntry.MeekCookieEncryptionPublicKey,
-			MeekObfuscatedKey:              serverEntry.MeekObfuscatedKey,
-			MeekObfuscatorPaddingSeed:      dialParams.MeekObfuscatorPaddingSeed,
-			CustomNetworkLatencyMultiplier: dialParams.CustomNetworkLatencyMultiplier,
+			DiagnosticID:                  serverEntry.GetDiagnosticID(),
+			ClientParameters:              config.clientParameters,
+			DialAddress:                   dialParams.MeekDialAddress,
+			UseQUIC:                       protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol),
+			QUICVersion:                   dialParams.QUICVersion,
+			UseHTTPS:                      protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol),
+			TLSProfile:                    dialParams.TLSProfile,
+			NoDefaultTLSSessionID:         dialParams.NoDefaultTLSSessionID,
+			RandomizedTLSProfileSeed:      dialParams.RandomizedTLSProfileSeed,
+			UseObfuscatedSessionTickets:   dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
+			SNIServerName:                 dialParams.MeekSNIServerName,
+			HostHeader:                    dialParams.MeekHostHeader,
+			TransformedHostName:           dialParams.MeekTransformedHostName,
+			ClientTunnelProtocol:          dialParams.TunnelProtocol,
+			MeekCookieEncryptionPublicKey: serverEntry.MeekCookieEncryptionPublicKey,
+			MeekObfuscatedKey:             serverEntry.MeekObfuscatedKey,
+			MeekObfuscatorPaddingSeed:     dialParams.MeekObfuscatorPaddingSeed,
+			NetworkLatencyMultiplier:      dialParams.NetworkLatencyMultiplier,
 		}
 
 		// Use an asynchronous callback to record the resolved IP address when

+ 32 - 32
psiphon/meekConn.go

@@ -133,9 +133,9 @@ type MeekConfig struct {
 	// incuding buffers are allocated.
 	RoundTripperOnly bool
 
-	// CustomNetworkLatencyMultiplier specifies a custom network latency
-	// multiplier to apply to client parameters used by this meek connection.
-	CustomNetworkLatencyMultiplier float64
+	// NetworkLatencyMultiplier specifies a custom network latency multiplier to
+	// apply to client parameters used by this meek connection.
+	NetworkLatencyMultiplier float64
 
 	// The following values are used to create the obfuscated meek cookie.
 
@@ -156,22 +156,22 @@ type MeekConfig struct {
 // MeekConn also operates in unfronted mode, in which plain HTTP connections are made without routing
 // through a CDN.
 type MeekConn struct {
-	clientParameters               *parameters.ClientParameters
-	customNetworkLatencyMultiplier float64
-	isQUIC                         bool
-	url                            *url.URL
-	additionalHeaders              http.Header
-	cookie                         *http.Cookie
-	cookieSize                     int
-	limitRequestPayloadLength      int
-	redialTLSProbability           float64
-	cachedTLSDialer                *cachedTLSDialer
-	transport                      transporter
-	mutex                          sync.Mutex
-	isClosed                       bool
-	runCtx                         context.Context
-	stopRunning                    context.CancelFunc
-	relayWaitGroup                 *sync.WaitGroup
+	clientParameters          *parameters.ClientParameters
+	networkLatencyMultiplier  float64
+	isQUIC                    bool
+	url                       *url.URL
+	additionalHeaders         http.Header
+	cookie                    *http.Cookie
+	cookieSize                int
+	limitRequestPayloadLength int
+	redialTLSProbability      float64
+	cachedTLSDialer           *cachedTLSDialer
+	transport                 transporter
+	mutex                     sync.Mutex
+	isClosed                  bool
+	runCtx                    context.Context
+	stopRunning               context.CancelFunc
+	relayWaitGroup            *sync.WaitGroup
 
 	// For round tripper mode
 	roundTripperOnly              bool
@@ -192,7 +192,7 @@ type MeekConn struct {
 }
 
 func (conn *MeekConn) getCustomClientParameters() parameters.ClientParametersAccessor {
-	return conn.clientParameters.GetCustom(conn.customNetworkLatencyMultiplier)
+	return conn.clientParameters.GetCustom(conn.networkLatencyMultiplier)
 }
 
 // transporter is implemented by both http.Transport and upstreamproxy.ProxyAuthTransport.
@@ -474,18 +474,18 @@ func DialMeek(
 	// Write() calls and relay() are synchronized in a similar way, using a single
 	// sendBuffer.
 	meek = &MeekConn{
-		clientParameters:               meekConfig.ClientParameters,
-		customNetworkLatencyMultiplier: meekConfig.CustomNetworkLatencyMultiplier,
-		isQUIC:                         isQUIC,
-		url:                            url,
-		additionalHeaders:              additionalHeaders,
-		cachedTLSDialer:                cachedTLSDialer,
-		transport:                      transport,
-		isClosed:                       false,
-		runCtx:                         runCtx,
-		stopRunning:                    stopRunning,
-		relayWaitGroup:                 new(sync.WaitGroup),
-		roundTripperOnly:               meekConfig.RoundTripperOnly,
+		clientParameters:         meekConfig.ClientParameters,
+		networkLatencyMultiplier: meekConfig.NetworkLatencyMultiplier,
+		isQUIC:                   isQUIC,
+		url:                      url,
+		additionalHeaders:        additionalHeaders,
+		cachedTLSDialer:          cachedTLSDialer,
+		transport:                transport,
+		isClosed:                 false,
+		runCtx:                   runCtx,
+		stopRunning:              stopRunning,
+		relayWaitGroup:           new(sync.WaitGroup),
+		roundTripperOnly:         meekConfig.RoundTripperOnly,
 	}
 
 	// stopRunning and cachedTLSDialer will now be closed in meek.Close()

+ 2 - 2
psiphon/notice.go

@@ -496,8 +496,8 @@ func noticeWithDialParameters(noticeType string, dialParams *DialParameters) {
 			args = append(args, "dialDuration", dialParams.DialDuration)
 		}
 
-		if dialParams.CustomNetworkLatencyMultiplier != 0.0 {
-			args = append(args, "networkLatencyMultiplier", dialParams.CustomNetworkLatencyMultiplier)
+		if dialParams.NetworkLatencyMultiplier != 0.0 {
+			args = append(args, "networkLatencyMultiplier", dialParams.NetworkLatencyMultiplier)
 		}
 
 		if dialParams.DialConnMetrics != nil {

+ 2 - 2
psiphon/serverApi.go

@@ -890,9 +890,9 @@ func getBaseAPIParameters(
 
 	params["candidate_number"] = strconv.Itoa(dialParams.CandidateNumber)
 
-	if dialParams.CustomNetworkLatencyMultiplier != 0.0 {
+	if dialParams.NetworkLatencyMultiplier != 0.0 {
 		params["network_latency_multiplier"] =
-			fmt.Sprintf("%f", dialParams.CustomNetworkLatencyMultiplier)
+			fmt.Sprintf("%f", dialParams.NetworkLatencyMultiplier)
 	}
 
 	if dialParams.DialConnMetrics != nil {

+ 1 - 1
psiphon/tunnel.go

@@ -112,7 +112,7 @@ func (tunnel *Tunnel) getCustomClientParameters() parameters.ClientParametersAcc
 
 func getCustomClientParameters(
 	config *Config, dialParams *DialParameters) parameters.ClientParametersAccessor {
-	return config.GetClientParameters().GetCustom(dialParams.CustomNetworkLatencyMultiplier)
+	return config.GetClientParameters().GetCustom(dialParams.NetworkLatencyMultiplier)
 }
 
 // ConnectTunnel first makes a network transport connection to the