|
|
|
|
@ -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) |
|
|
|
|
|