@@ -4,9 +4,11 @@ import (
44 "context"
55 "errors"
66 "fmt"
7+ "strings"
78 "time"
89
910 "github.com/google/uuid"
11+ "github.com/lucas-clemente/quic-go"
1012 "github.com/rs/zerolog"
1113
1214 "github.com/cloudflare/cloudflared/connection"
@@ -37,13 +39,14 @@ const (
3739// Supervisor manages non-declarative tunnels. Establishes TCP connections with the edge, and
3840// reconnects them if they disconnect.
3941type Supervisor struct {
40- cloudflaredUUID uuid.UUID
41- config * TunnelConfig
42- orchestrator * orchestration.Orchestrator
43- edgeIPs * edgediscovery.Edge
44- edgeTunnelServer EdgeTunnelServer
45- tunnelErrors chan tunnelError
46- tunnelsConnecting map [int ]chan struct {}
42+ cloudflaredUUID uuid.UUID
43+ config * TunnelConfig
44+ orchestrator * orchestration.Orchestrator
45+ edgeIPs * edgediscovery.Edge
46+ edgeTunnelServer EdgeTunnelServer
47+ tunnelErrors chan tunnelError
48+ tunnelsConnecting map [int ]chan struct {}
49+ tunnelsProtocolFallback map [int ]* protocolFallback
4750 // nextConnectedIndex and nextConnectedSignal are used to wait for all
4851 // currently-connecting tunnels to finish connecting so we can reset backoff timer
4952 nextConnectedIndex int
@@ -72,8 +75,10 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
7275 return nil , fmt .Errorf ("failed to generate cloudflared instance ID: %w" , err )
7376 }
7477
78+ isStaticEdge := len (config .EdgeAddrs ) > 0
79+
7580 var edgeIPs * edgediscovery.Edge
76- if len ( config . EdgeAddrs ) > 0 {
81+ if isStaticEdge { // static edge addresses
7782 edgeIPs , err = edgediscovery .StaticEdge (config .Log , config .EdgeAddrs )
7883 } else {
7984 edgeIPs , err = edgediscovery .ResolveEdge (config .Log , config .Region , config .EdgeIPVersion )
@@ -86,7 +91,9 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
8691 log := NewConnAwareLogger (config .Log , config .Observer )
8792
8893 var edgeAddrHandler EdgeAddrHandler
89- if config .EdgeIPVersion == allregions .IPv6Only || config .EdgeIPVersion == allregions .Auto {
94+ if isStaticEdge { // static edge addresses
95+ edgeAddrHandler = & IPAddrFallback {}
96+ } else if config .EdgeIPVersion == allregions .IPv6Only || config .EdgeIPVersion == allregions .Auto {
9097 edgeAddrHandler = & IPAddrFallback {}
9198 } else { // IPv4Only
9299 edgeAddrHandler = & DefaultAddrFallback {}
@@ -117,6 +124,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
117124 edgeTunnelServer : edgeTunnelServer ,
118125 tunnelErrors : make (chan tunnelError ),
119126 tunnelsConnecting : map [int ]chan struct {}{},
127+ tunnelsProtocolFallback : map [int ]* protocolFallback {},
120128 log : log ,
121129 logTransport : config .LogTransport ,
122130 reconnectCredentialManager : reconnectCredentialManager ,
@@ -178,6 +186,10 @@ func (s *Supervisor) Run(
178186 tunnelsActive ++
179187 continue
180188 }
189+ // Make sure we don't continue if there is no more fallback allowed
190+ if _ , retry := s .tunnelsProtocolFallback [tunnelError .index ].GetMaxBackoffDuration (ctx ); ! retry {
191+ continue
192+ }
181193 s .log .ConnAwareLogger ().Err (tunnelError .err ).Int (connection .LogFieldConnIndex , tunnelError .index ).Msg ("Connection terminated" )
182194 tunnelsWaiting = append (tunnelsWaiting , tunnelError .index )
183195 s .waitForNextTunnel (tunnelError .index )
@@ -232,6 +244,11 @@ func (s *Supervisor) initialize(
232244 s .log .Logger ().Info ().Msgf ("You requested %d HA connections but I can give you at most %d." , s .config .HAConnections , availableAddrs )
233245 s .config .HAConnections = availableAddrs
234246 }
247+ s .tunnelsProtocolFallback [0 ] = & protocolFallback {
248+ retry.BackoffHandler {MaxRetries : s .config .Retries },
249+ s .config .ProtocolSelector .Current (),
250+ false ,
251+ }
235252
236253 go s .startFirstTunnel (ctx , connectedSignal )
237254
@@ -249,6 +266,11 @@ func (s *Supervisor) initialize(
249266
250267 // At least one successful connection, so start the rest
251268 for i := 1 ; i < s .config .HAConnections ; i ++ {
269+ s .tunnelsProtocolFallback [i ] = & protocolFallback {
270+ retry.BackoffHandler {MaxRetries : s .config .Retries },
271+ s .config .ProtocolSelector .Current (),
272+ false ,
273+ }
252274 ch := signal .New (make (chan struct {}))
253275 go s .startTunnel (ctx , i , ch )
254276 time .Sleep (registrationInterval )
@@ -266,21 +288,44 @@ func (s *Supervisor) startFirstTunnel(
266288 err error
267289 )
268290 const firstConnIndex = 0
291+ isStaticEdge := len (s .config .EdgeAddrs ) > 0
269292 defer func () {
270293 s .tunnelErrors <- tunnelError {index : firstConnIndex , err : err }
271294 }()
272295
273- err = s .edgeTunnelServer .Serve (ctx , firstConnIndex , connectedSignal )
274-
275296 // If the first tunnel disconnects, keep restarting it.
276- for s .unusedIPs () {
297+ for {
298+ err = s .edgeTunnelServer .Serve (ctx , firstConnIndex , s .tunnelsProtocolFallback [firstConnIndex ], connectedSignal )
277299 if ctx .Err () != nil {
278300 return
279301 }
280302 if err == nil {
281303 return
282304 }
283- err = s .edgeTunnelServer .Serve (ctx , firstConnIndex , connectedSignal )
305+ // Make sure we don't continue if there is no more fallback allowed
306+ if _ , retry := s .tunnelsProtocolFallback [firstConnIndex ].GetMaxBackoffDuration (ctx ); ! retry {
307+ return
308+ }
309+ // Try again for Unauthorized errors because we hope them to be
310+ // transient due to edge propagation lag on new Tunnels.
311+ if strings .Contains (err .Error (), "Unauthorized" ) {
312+ continue
313+ }
314+ switch err .(type ) {
315+ case edgediscovery.ErrNoAddressesLeft :
316+ // If your provided addresses are not available, we will keep trying regardless.
317+ if ! isStaticEdge {
318+ return
319+ }
320+ case connection.DupConnRegisterTunnelError ,
321+ * quic.IdleTimeoutError ,
322+ edgediscovery.DialError ,
323+ * connection.EdgeQuicDialError :
324+ // Try again for these types of errors
325+ default :
326+ // Uncaught errors should bail startup
327+ return
328+ }
284329 }
285330}
286331
@@ -298,7 +343,7 @@ func (s *Supervisor) startTunnel(
298343 s .tunnelErrors <- tunnelError {index : index , err : err }
299344 }()
300345
301- err = s .edgeTunnelServer .Serve (ctx , uint8 (index ), connectedSignal )
346+ err = s .edgeTunnelServer .Serve (ctx , uint8 (index ), s . tunnelsProtocolFallback [ index ], connectedSignal )
302347}
303348
304349func (s * Supervisor ) newConnectedTunnelSignal (index int ) * signal.Signal {
0 commit comments