edgediscovery.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package edgediscovery
  2. import (
  3. "sync"
  4. "github.com/rs/zerolog"
  5. "github.com/cloudflare/cloudflared/edgediscovery/allregions"
  6. "github.com/cloudflare/cloudflared/management"
  7. )
  8. const (
  9. LogFieldConnIndex = "connIndex"
  10. LogFieldIPAddress = "ip"
  11. )
  12. var errNoAddressesLeft = ErrNoAddressesLeft{}
  13. type ErrNoAddressesLeft struct{}
  14. func (e ErrNoAddressesLeft) Error() string {
  15. return "there are no free edge addresses left to resolve to"
  16. }
  17. // Edge finds addresses on the Cloudflare edge and hands them out to connections.
  18. type Edge struct {
  19. regions *allregions.Regions
  20. sync.Mutex
  21. log *zerolog.Logger
  22. }
  23. // ------------------------------------
  24. // Constructors
  25. // ------------------------------------
  26. // ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated
  27. // to connections.
  28. func ResolveEdge(log *zerolog.Logger, region string, edgeIpVersion allregions.ConfigIPVersion) (*Edge, error) {
  29. regions, err := allregions.ResolveEdge(log, region, edgeIpVersion)
  30. if err != nil {
  31. return new(Edge), err
  32. }
  33. return &Edge{
  34. log: log,
  35. regions: regions,
  36. }, nil
  37. }
  38. // StaticEdge creates a list of edge addresses from the list of hostnames. Mainly used for testing connectivity.
  39. func StaticEdge(log *zerolog.Logger, hostnames []string) (*Edge, error) {
  40. regions, err := allregions.StaticEdge(hostnames, log)
  41. if err != nil {
  42. return new(Edge), err
  43. }
  44. return &Edge{
  45. log: log,
  46. regions: regions,
  47. }, nil
  48. }
  49. // ------------------------------------
  50. // Methods
  51. // ------------------------------------
  52. // GetAddrForRPC gives this connection an edge Addr.
  53. func (ed *Edge) GetAddrForRPC() (*allregions.EdgeAddr, error) {
  54. ed.Lock()
  55. defer ed.Unlock()
  56. addr := ed.regions.GetAnyAddress()
  57. if addr == nil {
  58. return nil, errNoAddressesLeft
  59. }
  60. return addr, nil
  61. }
  62. // GetAddr gives this proxy connection an edge Addr. Prefer Addrs this connection has already used.
  63. func (ed *Edge) GetAddr(connIndex int) (*allregions.EdgeAddr, error) {
  64. log := ed.log.With().
  65. Int(LogFieldConnIndex, connIndex).
  66. Int(management.EventTypeKey, int(management.Cloudflared)).
  67. Logger()
  68. ed.Lock()
  69. defer ed.Unlock()
  70. // If this connection has already used an edge addr, return it.
  71. if addr := ed.regions.AddrUsedBy(connIndex); addr != nil {
  72. log.Debug().IPAddr(LogFieldIPAddress, addr.UDP.IP).Msg("edge discovery: returning same edge address back to pool")
  73. return addr, nil
  74. }
  75. // Otherwise, give it an unused one
  76. addr := ed.regions.GetUnusedAddr(nil, connIndex)
  77. if addr == nil {
  78. log.Debug().Msg("edge discovery: no addresses left in pool to give proxy connection")
  79. return nil, errNoAddressesLeft
  80. }
  81. log.Debug().IPAddr(LogFieldIPAddress, addr.UDP.IP).Msg("edge discovery: giving new address to connection")
  82. return addr, nil
  83. }
  84. // GetDifferentAddr gives back the proxy connection's edge Addr and uses a new one.
  85. func (ed *Edge) GetDifferentAddr(connIndex int, hasConnectivityError bool) (*allregions.EdgeAddr, error) {
  86. log := ed.log.With().
  87. Int(LogFieldConnIndex, connIndex).
  88. Int(management.EventTypeKey, int(management.Cloudflared)).
  89. Logger()
  90. ed.Lock()
  91. defer ed.Unlock()
  92. oldAddr := ed.regions.AddrUsedBy(connIndex)
  93. if oldAddr != nil {
  94. ed.regions.GiveBack(oldAddr, hasConnectivityError)
  95. }
  96. addr := ed.regions.GetUnusedAddr(oldAddr, connIndex)
  97. if addr == nil {
  98. log.Debug().Msg("edge discovery: no addresses left in pool to give proxy connection")
  99. // note: if oldAddr were not nil, it will become available on the next iteration
  100. return nil, errNoAddressesLeft
  101. }
  102. log.Debug().
  103. IPAddr(LogFieldIPAddress, addr.UDP.IP).
  104. Int("available", ed.regions.AvailableAddrs()).
  105. Msg("edge discovery: giving new address to connection")
  106. return addr, nil
  107. }
  108. // AvailableAddrs returns how many unused addresses there are left.
  109. func (ed *Edge) AvailableAddrs() int {
  110. ed.Lock()
  111. defer ed.Unlock()
  112. return ed.regions.AvailableAddrs()
  113. }
  114. // GiveBack the address so that other connections can use it.
  115. // Returns true if the address is in this edge.
  116. func (ed *Edge) GiveBack(addr *allregions.EdgeAddr, hasConnectivityError bool) bool {
  117. ed.Lock()
  118. defer ed.Unlock()
  119. ed.log.Debug().
  120. Int(management.EventTypeKey, int(management.Cloudflared)).
  121. IPAddr(LogFieldIPAddress, addr.UDP.IP).
  122. Msg("edge discovery: gave back address to the pool")
  123. return ed.regions.GiveBack(addr, hasConnectivityError)
  124. }