|
|
|
@ -36,6 +36,7 @@ import (
|
|
|
|
|
"go4.org/mem"
|
|
|
|
|
"golang.org/x/crypto/nacl/box"
|
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
|
"golang.org/x/time/rate"
|
|
|
|
|
"inet.af/netaddr"
|
|
|
|
|
"tailscale.com/client/tailscale"
|
|
|
|
|
"tailscale.com/disco"
|
|
|
|
@ -118,6 +119,8 @@ type Server struct {
|
|
|
|
|
curClients expvar.Int
|
|
|
|
|
curHomeClients expvar.Int // ones with preferred
|
|
|
|
|
clientsReplaced expvar.Int
|
|
|
|
|
clientsReplaceLimited expvar.Int
|
|
|
|
|
clientsReplaceSleeping expvar.Int
|
|
|
|
|
unknownFrames expvar.Int
|
|
|
|
|
homeMovesIn expvar.Int // established clients announce home server moves in
|
|
|
|
|
homeMovesOut expvar.Int // established clients announce home server moves out
|
|
|
|
@ -346,14 +349,28 @@ func (s *Server) initMetacert() {
|
|
|
|
|
func (s *Server) MetaCert() []byte { return s.metaCert }
|
|
|
|
|
|
|
|
|
|
// registerClient notes that client c is now authenticated and ready for packets.
|
|
|
|
|
// If c's public key was already connected with a different connection, the prior one is closed.
|
|
|
|
|
func (s *Server) registerClient(c *sclient) {
|
|
|
|
|
//
|
|
|
|
|
// If c's public key was already connected with a different
|
|
|
|
|
// connection, the prior one is closed, unless it's fighting rapidly
|
|
|
|
|
// with another client with the same key, in which case the returned
|
|
|
|
|
// ok is false, and the caller should wait the provided duration
|
|
|
|
|
// before trying again.
|
|
|
|
|
func (s *Server) registerClient(c *sclient) (ok bool, d time.Duration) {
|
|
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
old := s.clients[c.key]
|
|
|
|
|
if old == nil {
|
|
|
|
|
c.logf("adding connection")
|
|
|
|
|
} else {
|
|
|
|
|
// Take over the old rate limiter, discarding the one
|
|
|
|
|
// our caller just made.
|
|
|
|
|
c.replaceLimiter = old.replaceLimiter
|
|
|
|
|
if rr := c.replaceLimiter.ReserveN(timeNow(), 1); rr.OK() {
|
|
|
|
|
if d := rr.DelayFrom(timeNow()); d > 0 {
|
|
|
|
|
s.clientsReplaceLimited.Add(1)
|
|
|
|
|
return false, d
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s.clientsReplaced.Add(1)
|
|
|
|
|
c.logf("adding connection, replacing %s", old.remoteAddr)
|
|
|
|
|
go old.nc.Close()
|
|
|
|
@ -365,6 +382,7 @@ func (s *Server) registerClient(c *sclient) {
|
|
|
|
|
s.keyOfAddr[c.remoteIPPort] = c.key
|
|
|
|
|
s.curClients.Add(1)
|
|
|
|
|
s.broadcastPeerStateChangeLocked(c.key, true)
|
|
|
|
|
return true, 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// broadcastPeerStateChangeLocked enqueues a message to all watchers
|
|
|
|
@ -490,7 +508,14 @@ func (s *Server) accept(nc Conn, brw *bufio.ReadWriter, remoteAddr string, connN
|
|
|
|
|
discoSendQueue: make(chan pkt, perClientSendQueueDepth),
|
|
|
|
|
peerGone: make(chan key.Public),
|
|
|
|
|
canMesh: clientInfo.MeshKey != "" && clientInfo.MeshKey == s.meshKey,
|
|
|
|
|
|
|
|
|
|
// Allow kicking out previous connections once a
|
|
|
|
|
// minute, with a very high burst of 100. Once a
|
|
|
|
|
// minute is less than the client's 2 minute
|
|
|
|
|
// inactivity timeout.
|
|
|
|
|
replaceLimiter: rate.NewLimiter(rate.Every(time.Minute), 100),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.canMesh {
|
|
|
|
|
c.meshUpdate = make(chan struct{})
|
|
|
|
|
}
|
|
|
|
@ -498,7 +523,15 @@ func (s *Server) accept(nc Conn, brw *bufio.ReadWriter, remoteAddr string, connN
|
|
|
|
|
c.info = *clientInfo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.registerClient(c)
|
|
|
|
|
for {
|
|
|
|
|
ok, d := s.registerClient(c)
|
|
|
|
|
if ok {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
s.clientsReplaceSleeping.Add(1)
|
|
|
|
|
timeSleep(d)
|
|
|
|
|
s.clientsReplaceSleeping.Add(-1)
|
|
|
|
|
}
|
|
|
|
|
defer s.unregisterClient(c)
|
|
|
|
|
|
|
|
|
|
err = s.sendServerInfo(bw, clientKey)
|
|
|
|
@ -509,6 +542,12 @@ func (s *Server) accept(nc Conn, brw *bufio.ReadWriter, remoteAddr string, connN
|
|
|
|
|
return c.run(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for testing
|
|
|
|
|
var (
|
|
|
|
|
timeSleep = time.Sleep
|
|
|
|
|
timeNow = time.Now
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// run serves the client until there's an error.
|
|
|
|
|
// If the client hangs up or the server is closed, run returns nil, otherwise run returns an error.
|
|
|
|
|
func (c *sclient) run(ctx context.Context) error {
|
|
|
|
@ -952,6 +991,11 @@ type sclient struct {
|
|
|
|
|
meshUpdate chan struct{} // write request to write peerStateChange
|
|
|
|
|
canMesh bool // clientInfo had correct mesh token for inter-region routing
|
|
|
|
|
|
|
|
|
|
// replaceLimiter controls how quickly two connections with
|
|
|
|
|
// the same client key can kick each other off the server by
|
|
|
|
|
// taking over ownership of a key.
|
|
|
|
|
replaceLimiter *rate.Limiter
|
|
|
|
|
|
|
|
|
|
// Owned by run, not thread-safe.
|
|
|
|
|
br *bufio.Reader
|
|
|
|
|
connectedAt time.Time
|
|
|
|
@ -1351,6 +1395,8 @@ func (s *Server) ExpVar() expvar.Var {
|
|
|
|
|
m.Set("gauge_clients_remote", expvar.Func(func() interface{} { return len(s.clientsMesh) - len(s.clients) }))
|
|
|
|
|
m.Set("accepts", &s.accepts)
|
|
|
|
|
m.Set("clients_replaced", &s.clientsReplaced)
|
|
|
|
|
m.Set("clients_replace_limited", &s.clientsReplaceLimited)
|
|
|
|
|
m.Set("gauge_clients_replace_sleeping", &s.clientsReplaceSleeping)
|
|
|
|
|
m.Set("bytes_received", &s.bytesRecv)
|
|
|
|
|
m.Set("bytes_sent", &s.bytesSent)
|
|
|
|
|
m.Set("packets_dropped", &s.packetsDropped)
|
|
|
|
|