package allregions import "time" const ( timeoutDuration = 10 * time.Minute ) // Region contains cloudflared edge addresses. The edge is partitioned into several regions for // redundancy purposes. type Region struct { primaryIsActive bool active AddrSet primary AddrSet secondary AddrSet primaryTimeout time.Time timeoutDuration time.Duration } // NewRegion creates a region with the given addresses, which are all unused. func NewRegion(addrs []*EdgeAddr, overrideIPVersion ConfigIPVersion) Region { // The zero value of UsedBy is Unused(), so we can just initialize the map's values with their // zero values. connForv4 := make(AddrSet) connForv6 := make(AddrSet) systemPreference := V6 for i, addr := range addrs { if i == 0 { // First family of IPs returned is system preference of IP systemPreference = addr.IPVersion } switch addr.IPVersion { case V4: connForv4[addr] = Unused() case V6: connForv6[addr] = Unused() } } // Process as system preference var primary AddrSet var secondary AddrSet switch systemPreference { case V4: primary = connForv4 secondary = connForv6 case V6: primary = connForv6 secondary = connForv4 } // Override with provided preference switch overrideIPVersion { case IPv4Only: primary = connForv4 secondary = make(AddrSet) // empty case IPv6Only: primary = connForv6 secondary = make(AddrSet) // empty case Auto: // no change default: // no change } return Region{ primaryIsActive: true, active: primary, primary: primary, secondary: secondary, timeoutDuration: timeoutDuration, } } // AddrUsedBy finds the address used by the given connection in this region. // Returns nil if the connection isn't using any IP. func (r *Region) AddrUsedBy(connID int) *EdgeAddr { edgeAddr := r.primary.AddrUsedBy(connID) if edgeAddr == nil { edgeAddr = r.secondary.AddrUsedBy(connID) } return edgeAddr } // AvailableAddrs counts how many unused addresses this region contains. func (r Region) AvailableAddrs() int { return r.active.AvailableAddrs() } // AssignAnyAddress returns a random unused address in this region now // assigned to the connID excluding the provided EdgeAddr. // Returns nil if all addresses are in use for the region. func (r Region) AssignAnyAddress(connID int, excluding *EdgeAddr) *EdgeAddr { if addr := r.active.GetUnusedIP(excluding); addr != nil { r.active.Use(addr, connID) return addr } return nil } // GetAnyAddress returns an arbitrary address from the region. func (r Region) GetAnyAddress() *EdgeAddr { return r.active.GetAnyAddress() } // GiveBack the address, ensuring it is no longer assigned to an IP. // Returns true if the address is in this region. func (r *Region) GiveBack(addr *EdgeAddr, hasConnectivityError bool) (ok bool) { if ok = r.primary.GiveBack(addr); !ok { // Attempt to give back the address in the secondary set if ok = r.secondary.GiveBack(addr); !ok { // Address is not in this region return } } // No connectivity error: no worry if !hasConnectivityError { return } // If using primary and returned address is IPv6 and secondary is available if r.primaryIsActive && addr.IPVersion == V6 && len(r.secondary) > 0 { r.active = r.secondary r.primaryIsActive = false r.primaryTimeout = time.Now().Add(r.timeoutDuration) return } // Do nothing for IPv4 or if secondary is empty if r.primaryIsActive { return } // Immediately return to primary pool, regardless of current primary timeout if addr.IPVersion == V4 { activatePrimary(r) return } // Timeout exceeded and can be reset to primary pool if r.primaryTimeout.Before(time.Now()) { activatePrimary(r) return } return } // activatePrimary sets the primary set to the active set and resets the timeout. func activatePrimary(r *Region) { r.active = r.primary r.primaryIsActive = true r.primaryTimeout = time.Now() // reset timeout }