region.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package allregions
  2. import "time"
  3. const (
  4. timeoutDuration = 10 * time.Minute
  5. )
  6. // Region contains cloudflared edge addresses. The edge is partitioned into several regions for
  7. // redundancy purposes.
  8. type Region struct {
  9. primaryIsActive bool
  10. active AddrSet
  11. primary AddrSet
  12. secondary AddrSet
  13. primaryTimeout time.Time
  14. timeoutDuration time.Duration
  15. }
  16. // NewRegion creates a region with the given addresses, which are all unused.
  17. func NewRegion(addrs []*EdgeAddr, overrideIPVersion ConfigIPVersion) Region {
  18. // The zero value of UsedBy is Unused(), so we can just initialize the map's values with their
  19. // zero values.
  20. connForv4 := make(AddrSet)
  21. connForv6 := make(AddrSet)
  22. systemPreference := V6
  23. for i, addr := range addrs {
  24. if i == 0 {
  25. // First family of IPs returned is system preference of IP
  26. systemPreference = addr.IPVersion
  27. }
  28. switch addr.IPVersion {
  29. case V4:
  30. connForv4[addr] = Unused()
  31. case V6:
  32. connForv6[addr] = Unused()
  33. }
  34. }
  35. // Process as system preference
  36. var primary AddrSet
  37. var secondary AddrSet
  38. switch systemPreference {
  39. case V4:
  40. primary = connForv4
  41. secondary = connForv6
  42. case V6:
  43. primary = connForv6
  44. secondary = connForv4
  45. }
  46. // Override with provided preference
  47. switch overrideIPVersion {
  48. case IPv4Only:
  49. primary = connForv4
  50. secondary = make(AddrSet) // empty
  51. case IPv6Only:
  52. primary = connForv6
  53. secondary = make(AddrSet) // empty
  54. case Auto:
  55. // no change
  56. default:
  57. // no change
  58. }
  59. return Region{
  60. primaryIsActive: true,
  61. active: primary,
  62. primary: primary,
  63. secondary: secondary,
  64. timeoutDuration: timeoutDuration,
  65. }
  66. }
  67. // AddrUsedBy finds the address used by the given connection in this region.
  68. // Returns nil if the connection isn't using any IP.
  69. func (r *Region) AddrUsedBy(connID int) *EdgeAddr {
  70. edgeAddr := r.primary.AddrUsedBy(connID)
  71. if edgeAddr == nil {
  72. edgeAddr = r.secondary.AddrUsedBy(connID)
  73. }
  74. return edgeAddr
  75. }
  76. // AvailableAddrs counts how many unused addresses this region contains.
  77. func (r Region) AvailableAddrs() int {
  78. return r.active.AvailableAddrs()
  79. }
  80. // AssignAnyAddress returns a random unused address in this region now
  81. // assigned to the connID excluding the provided EdgeAddr.
  82. // Returns nil if all addresses are in use for the region.
  83. func (r Region) AssignAnyAddress(connID int, excluding *EdgeAddr) *EdgeAddr {
  84. if addr := r.active.GetUnusedIP(excluding); addr != nil {
  85. r.active.Use(addr, connID)
  86. return addr
  87. }
  88. return nil
  89. }
  90. // GetAnyAddress returns an arbitrary address from the region.
  91. func (r Region) GetAnyAddress() *EdgeAddr {
  92. return r.active.GetAnyAddress()
  93. }
  94. // GiveBack the address, ensuring it is no longer assigned to an IP.
  95. // Returns true if the address is in this region.
  96. func (r *Region) GiveBack(addr *EdgeAddr, hasConnectivityError bool) (ok bool) {
  97. if ok = r.primary.GiveBack(addr); !ok {
  98. // Attempt to give back the address in the secondary set
  99. if ok = r.secondary.GiveBack(addr); !ok {
  100. // Address is not in this region
  101. return
  102. }
  103. }
  104. // No connectivity error: no worry
  105. if !hasConnectivityError {
  106. return
  107. }
  108. // If using primary and returned address is IPv6 and secondary is available
  109. if r.primaryIsActive && addr.IPVersion == V6 && len(r.secondary) > 0 {
  110. r.active = r.secondary
  111. r.primaryIsActive = false
  112. r.primaryTimeout = time.Now().Add(r.timeoutDuration)
  113. return
  114. }
  115. // Do nothing for IPv4 or if secondary is empty
  116. if r.primaryIsActive {
  117. return
  118. }
  119. // Immediately return to primary pool, regardless of current primary timeout
  120. if addr.IPVersion == V4 {
  121. activatePrimary(r)
  122. return
  123. }
  124. // Timeout exceeded and can be reset to primary pool
  125. if r.primaryTimeout.Before(time.Now()) {
  126. activatePrimary(r)
  127. return
  128. }
  129. return
  130. }
  131. // activatePrimary sets the primary set to the active set and resets the timeout.
  132. func activatePrimary(r *Region) {
  133. r.active = r.primary
  134. r.primaryIsActive = true
  135. r.primaryTimeout = time.Now() // reset timeout
  136. }