2020-02-06 00:55:26 +00:00
|
|
|
package edgediscovery
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
2020-04-29 20:51:32 +00:00
|
|
|
"github.com/cloudflare/cloudflared/logger"
|
2020-02-06 00:55:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
subsystem = "edgediscovery"
|
|
|
|
)
|
|
|
|
|
|
|
|
var errNoAddressesLeft = fmt.Errorf("There are no free edge addresses left")
|
|
|
|
|
|
|
|
// Edge finds addresses on the Cloudflare edge and hands them out to connections.
|
|
|
|
type Edge struct {
|
|
|
|
regions *allregions.Regions
|
|
|
|
sync.Mutex
|
2020-04-29 20:51:32 +00:00
|
|
|
logger logger.Service
|
2020-02-06 00:55:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
// Constructors
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
// ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated
|
|
|
|
// to connections.
|
2020-04-29 20:51:32 +00:00
|
|
|
func ResolveEdge(l logger.Service) (*Edge, error) {
|
|
|
|
regions, err := allregions.ResolveEdge(l)
|
2020-02-06 00:55:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return new(Edge), err
|
|
|
|
}
|
|
|
|
return &Edge{
|
2020-04-29 20:51:32 +00:00
|
|
|
logger: l,
|
2020-02-06 00:55:26 +00:00
|
|
|
regions: regions,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StaticEdge creates a list of edge addresses from the list of hostnames. Mainly used for testing connectivity.
|
2020-04-29 20:51:32 +00:00
|
|
|
func StaticEdge(l logger.Service, hostnames []string) (*Edge, error) {
|
2020-10-30 16:23:26 +00:00
|
|
|
regions, err := allregions.StaticEdge(hostnames, l)
|
2020-02-06 00:55:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return new(Edge), err
|
|
|
|
}
|
|
|
|
return &Edge{
|
2020-04-29 20:51:32 +00:00
|
|
|
logger: l,
|
2020-02-06 00:55:26 +00:00
|
|
|
regions: regions,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing.
|
2020-04-29 20:51:32 +00:00
|
|
|
func MockEdge(l logger.Service, addrs []*net.TCPAddr) *Edge {
|
2020-02-06 00:55:26 +00:00
|
|
|
regions := allregions.NewNoResolve(addrs)
|
|
|
|
return &Edge{
|
2020-04-29 20:51:32 +00:00
|
|
|
logger: l,
|
2020-02-06 00:55:26 +00:00
|
|
|
regions: regions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
// Methods
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
// GetAddrForRPC gives this connection an edge Addr.
|
|
|
|
func (ed *Edge) GetAddrForRPC() (*net.TCPAddr, error) {
|
|
|
|
ed.Lock()
|
|
|
|
defer ed.Unlock()
|
|
|
|
addr := ed.regions.GetAnyAddress()
|
|
|
|
if addr == nil {
|
|
|
|
return nil, errNoAddressesLeft
|
|
|
|
}
|
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAddr gives this proxy connection an edge Addr. Prefer Addrs this connection has already used.
|
|
|
|
func (ed *Edge) GetAddr(connID int) (*net.TCPAddr, error) {
|
|
|
|
ed.Lock()
|
|
|
|
defer ed.Unlock()
|
|
|
|
|
|
|
|
// If this connection has already used an edge addr, return it.
|
|
|
|
if addr := ed.regions.AddrUsedBy(connID); addr != nil {
|
2020-04-29 20:51:32 +00:00
|
|
|
ed.logger.Debugf("edgediscovery - GetAddr: Returning same address back to proxy connection: connID: %d", connID)
|
2020-02-06 00:55:26 +00:00
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, give it an unused one
|
|
|
|
addr := ed.regions.GetUnusedAddr(nil, connID)
|
|
|
|
if addr == nil {
|
2020-04-29 20:51:32 +00:00
|
|
|
ed.logger.Debugf("edgediscovery - GetAddr: No addresses left to give proxy connection: connID: %d", connID)
|
2020-02-06 00:55:26 +00:00
|
|
|
return nil, errNoAddressesLeft
|
|
|
|
}
|
2020-04-29 20:51:32 +00:00
|
|
|
ed.logger.Debugf("edgediscovery - GetAddr: Giving connection its new address %s: connID: %d", addr, connID)
|
2020-02-06 00:55:26 +00:00
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDifferentAddr gives back the proxy connection's edge Addr and uses a new one.
|
|
|
|
func (ed *Edge) GetDifferentAddr(connID int) (*net.TCPAddr, error) {
|
|
|
|
ed.Lock()
|
|
|
|
defer ed.Unlock()
|
|
|
|
|
|
|
|
oldAddr := ed.regions.AddrUsedBy(connID)
|
|
|
|
if oldAddr != nil {
|
|
|
|
ed.regions.GiveBack(oldAddr)
|
|
|
|
}
|
|
|
|
addr := ed.regions.GetUnusedAddr(oldAddr, connID)
|
|
|
|
if addr == nil {
|
2020-04-29 20:51:32 +00:00
|
|
|
ed.logger.Debugf("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection: connID: %d", connID)
|
2020-04-27 19:25:37 +00:00
|
|
|
// note: if oldAddr were not nil, it will become available on the next iteration
|
2020-02-06 00:55:26 +00:00
|
|
|
return nil, errNoAddressesLeft
|
|
|
|
}
|
2020-04-29 20:51:32 +00:00
|
|
|
ed.logger.Debugf("edgediscovery - GetDifferentAddr: Giving connection its new address %s: connID: %d", addr, connID)
|
2020-02-06 00:55:26 +00:00
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AvailableAddrs returns how many unused addresses there are left.
|
|
|
|
func (ed *Edge) AvailableAddrs() int {
|
|
|
|
ed.Lock()
|
|
|
|
defer ed.Unlock()
|
|
|
|
return ed.regions.AvailableAddrs()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GiveBack the address so that other connections can use it.
|
|
|
|
// Returns true if the address is in this edge.
|
|
|
|
func (ed *Edge) GiveBack(addr *net.TCPAddr) bool {
|
|
|
|
ed.Lock()
|
|
|
|
defer ed.Unlock()
|
2020-04-29 20:51:32 +00:00
|
|
|
ed.logger.Debug("edgediscovery - GiveBack: Address now unused")
|
2020-02-06 00:55:26 +00:00
|
|
|
return ed.regions.GiveBack(addr)
|
|
|
|
}
|