@@ -361,6 +361,10 @@ const (
361361 // and then a success(...S S S S F S). The confidence in the targetConfidence window will be equal to
362362 // targetConfidence, the last F and S cancel each other, and we won't probe again for maxProbeInterval.
363363 maxRecentDialsWindow = targetConfidence + 2
364+ // secondaryAddrsScalingFactor is the multiplier applied to secondary address dial outcomes. For secondary
365+ // addr, if the primary addr is reachable, a single successful dial is enough to consider the secondary addr
366+ // reachable.
367+ secondaryAddrsScalingFactor = targetConfidence
364368 // highConfidenceAddrProbeInterval is the maximum interval between probes for an address
365369 highConfidenceAddrProbeInterval = 1 * time .Hour
366370 // maxProbeResultTTL is the maximum time to keep probe results for an address
@@ -380,7 +384,8 @@ type probeManager struct {
380384 inProgressProbes map [string ]int // addr -> count
381385 inProgressProbesTotal int
382386 statuses map [string ]* addrStatus
383- addrs []ma.Multiaddr
387+ primaryAddrs []ma.Multiaddr
388+ secondaryAddrs []ma.Multiaddr
384389}
385390
386391// newProbeManager creates a new probe manager.
@@ -397,7 +402,19 @@ func (m *probeManager) AppendConfirmedAddrs(reachable, unreachable, unknown []ma
397402 m .mx .Lock ()
398403 defer m .mx .Unlock ()
399404
400- for _ , a := range m .addrs {
405+ for _ , a := range m .primaryAddrs {
406+ s := m .statuses [string (a .Bytes ())]
407+ s .RemoveBefore (m .now ().Add (- maxProbeResultTTL )) // cleanup stale results
408+ switch s .Reachability () {
409+ case network .ReachabilityPublic :
410+ reachable = append (reachable , a )
411+ case network .ReachabilityPrivate :
412+ unreachable = append (unreachable , a )
413+ case network .ReachabilityUnknown :
414+ unknown = append (unknown , a )
415+ }
416+ }
417+ for _ , a := range m .secondaryAddrs {
401418 s := m .statuses [string (a .Bytes ())]
402419 s .RemoveBefore (m .now ().Add (- maxProbeResultTTL )) // cleanup stale results
403420 switch s .Reachability () {
@@ -425,9 +442,20 @@ func (m *probeManager) UpdateAddrs(addrs []ma.Multiaddr) {
425442 statuses [k ] = & addrStatus {Addr : addr }
426443 } else {
427444 statuses [k ] = m .statuses [k ]
445+ // our addresses have changed, we might have removed the primary address
446+ statuses [k ].primary = nil
447+ }
448+ }
449+ assignPrimaryAddrs (statuses )
450+ m .primaryAddrs = m .primaryAddrs [:0 ]
451+ m .secondaryAddrs = m .secondaryAddrs [:0 ]
452+ for _ , a := range addrs {
453+ if statuses [string (a .Bytes ())].primary == nil {
454+ m .primaryAddrs = append (m .primaryAddrs , a )
455+ } else {
456+ m .secondaryAddrs = append (m .secondaryAddrs , a )
428457 }
429458 }
430- m .addrs = addrs
431459 m .statuses = statuses
432460}
433461
@@ -439,32 +467,50 @@ func (m *probeManager) GetProbe() probe {
439467 defer m .mx .Unlock ()
440468
441469 now := m .now ()
442- for i , a := range m .addrs {
443- ab := a .Bytes ()
444- pc := m .statuses [string (ab )].RequiredProbeCount (now )
445- if m .inProgressProbes [string (ab )] >= pc {
446- continue
470+ reqs := make (probe , 0 , maxAddrsPerRequest )
471+ reqs = m .appendRequestsToProbe (reqs , m .primaryAddrs , now )
472+ reqs = m .appendRequestsToProbe (reqs , m .secondaryAddrs , now )
473+ if len (reqs ) >= maxAddrsPerRequest {
474+ reqs = reqs [:maxAddrsPerRequest ]
475+ }
476+ return reqs
477+ }
478+
479+ func (m * probeManager ) appendRequestsToProbe (reqs probe , addrs []ma.Multiaddr , now time.Time ) probe {
480+ n := len (addrs )
481+ i := n
482+ if len (reqs ) == 0 {
483+ for i = 0 ; i < n ; i ++ {
484+ s := m .statuses [string (addrs [i ].Bytes ())]
485+ pc := s .RequiredProbeCount (now )
486+ if pc == 0 || m .inProgressProbes [string (addrs [i ].Bytes ())] >= pc {
487+ continue
488+ }
489+ reqs = append (reqs , autonatv2.Request {Addr : addrs [i ], SendDialData : true })
490+ break
447491 }
448- reqs := make (probe , 0 , maxAddrsPerRequest )
449- reqs = append (reqs , autonatv2.Request {Addr : a , SendDialData : true })
450- // We have the first(primary) address. Append other addresses, ignoring inprogress probes
451- // on secondary addresses. The expectation is that the primary address will
452- // be dialed.
453- for j := 1 ; j < len (m .addrs ); j ++ {
454- k := (i + j ) % len (m .addrs )
455- ab := m .addrs [k ].Bytes ()
456- pc := m .statuses [string (ab )].RequiredProbeCount (now )
492+ }
493+
494+ // We have the first address. Append other addresses, ignoring inprogress probes.
495+ // The expectation is that the first address will be dialed.
496+ if len (reqs ) > 0 {
497+ for j := range n {
498+ k := (j + i ) % n
499+ if k == i {
500+ continue
501+ }
502+ s := m .statuses [string (addrs [k ].Bytes ())]
503+ pc := s .RequiredProbeCount (now )
457504 if pc == 0 {
458505 continue
459506 }
460- reqs = append (reqs , autonatv2.Request {Addr : m . addrs [k ], SendDialData : true })
507+ reqs = append (reqs , autonatv2.Request {Addr : addrs [k ], SendDialData : true })
461508 if len (reqs ) >= maxAddrsPerRequest {
462509 break
463510 }
464511 }
465- return reqs
466512 }
467- return nil
513+ return reqs
468514}
469515
470516// MarkProbeInProgress should be called when a probe is started.
@@ -499,10 +545,10 @@ func (m *probeManager) CompleteProbe(reqs probe, res autonatv2.Result, err error
499545 defer m .mx .Unlock ()
500546
501547 // decrement in-progress count for the first address
502- primaryAddrKey := string (reqs [0 ].Addr .Bytes ())
503- m .inProgressProbes [primaryAddrKey ]--
504- if m .inProgressProbes [primaryAddrKey ] <= 0 {
505- delete (m .inProgressProbes , primaryAddrKey )
548+ firstAddrKey := string (reqs [0 ].Addr .Bytes ())
549+ m .inProgressProbes [firstAddrKey ]--
550+ if m .inProgressProbes [firstAddrKey ] <= 0 {
551+ delete (m .inProgressProbes , firstAddrKey )
506552 }
507553 m .inProgressProbesTotal --
508554
@@ -511,17 +557,17 @@ func (m *probeManager) CompleteProbe(reqs probe, res autonatv2.Result, err error
511557 return
512558 }
513559
514- // Consider only primary address as refused. This increases the number of
560+ // Consider only first address as refused. This increases the number of
515561 // refused probes, but refused probes are cheap for a server as no dials are made.
516562 if res .AllAddrsRefused {
517- if s , ok := m .statuses [primaryAddrKey ]; ok {
563+ if s , ok := m .statuses [firstAddrKey ]; ok {
518564 s .AddRefusal (now )
519565 }
520566 return
521567 }
522568 dialAddrKey := string (res .Addr .Bytes ())
523- if dialAddrKey != primaryAddrKey {
524- if s , ok := m .statuses [primaryAddrKey ]; ok {
569+ if dialAddrKey != firstAddrKey {
570+ if s , ok := m .statuses [firstAddrKey ]; ok {
525571 s .AddRefusal (now )
526572 }
527573 }
@@ -539,6 +585,7 @@ type dialOutcome struct {
539585
540586type addrStatus struct {
541587 Addr ma.Multiaddr
588+ primary * addrStatus
542589 lastRefusalTime time.Time
543590 consecutiveRefusals int
544591 dialTimes []time.Time
@@ -670,6 +717,15 @@ func (s *addrStatus) reachabilityAndCounts() (rch network.Reachability, successe
670717 failures ++
671718 }
672719 }
720+ if s .primary != nil {
721+ prch , _ , _ := s .primary .reachabilityAndCounts ()
722+ switch prch {
723+ case network .ReachabilityPublic :
724+ successes *= secondaryAddrsScalingFactor
725+ case network .ReachabilityPrivate :
726+ failures *= secondaryAddrsScalingFactor
727+ }
728+ }
673729 if successes - failures >= minConfidence {
674730 return network .ReachabilityPublic , successes , failures
675731 }
@@ -678,3 +734,60 @@ func (s *addrStatus) reachabilityAndCounts() (rch network.Reachability, successe
678734 }
679735 return network .ReachabilityUnknown , successes , failures
680736}
737+
738+ var errNotTW = errors .New ("not a thinwaist address" )
739+
740+ func thinWaistPart (a ma.Multiaddr ) (ma.Multiaddr , error ) {
741+ if len (a ) < 2 {
742+ return nil , errNotTW
743+ }
744+ if c0 , c1 := a [0 ].Code (), a [1 ].Code (); (c0 != ma .P_IP4 && c0 != ma .P_IP6 ) || (c1 != ma .P_TCP && c1 != ma .P_UDP ) {
745+ return nil , errNotTW
746+ }
747+ return a [:2 ], nil
748+ }
749+
750+ func assignPrimaryAddrs (statuses map [string ]* addrStatus ) {
751+ twMap := make (map [string ][]ma.Multiaddr , len (statuses ))
752+ for _ , s := range statuses {
753+ twp , err := thinWaistPart (s .Addr )
754+ if err != nil {
755+ continue
756+ }
757+ twMap [string (twp .Bytes ())] = append (twMap [string (twp .Bytes ())], s .Addr )
758+ }
759+
760+ score := func (a ma.Multiaddr ) int {
761+ score := 0
762+ for _ , p := range a {
763+ switch p .Code () {
764+ case ma .P_QUIC_V1 , ma .P_TCP :
765+ score += 1
766+ case ma .P_WEBTRANSPORT :
767+ score += 1 << 1
768+ case ma .P_WEBRTC :
769+ score += 1 << 2
770+ case ma .P_WS , ma .P_WSS :
771+ score += 1 << 3
772+ }
773+ }
774+ if score == 0 {
775+ return 1 << 20
776+ }
777+ return score
778+ }
779+ for _ , addrs := range twMap {
780+ if len (addrs ) <= 1 {
781+ continue
782+ }
783+ slices .SortFunc (addrs , func (a , b ma.Multiaddr ) int {
784+ return score (a ) - score (b )
785+ })
786+ primary := addrs [0 ]
787+ ps := statuses [string (primary .Bytes ())]
788+ for _ , a := range addrs [1 :] {
789+ s := statuses [string (a .Bytes ())]
790+ s .primary = ps
791+ }
792+ }
793+ }
0 commit comments