|
|
|
@ -97,36 +97,34 @@ type Server struct {
|
|
|
|
|
metaCert []byte // the encoded x509 cert to send after LetsEncrypt cert+intermediate
|
|
|
|
|
|
|
|
|
|
// Counters:
|
|
|
|
|
_ [pad32bit]byte
|
|
|
|
|
packetsSent, bytesSent expvar.Int
|
|
|
|
|
packetsRecv, bytesRecv expvar.Int
|
|
|
|
|
packetsRecvByKind metrics.LabelMap
|
|
|
|
|
packetsRecvDisco *expvar.Int
|
|
|
|
|
packetsRecvOther *expvar.Int
|
|
|
|
|
_ [pad32bit]byte
|
|
|
|
|
packetsDropped expvar.Int
|
|
|
|
|
packetsDroppedReason metrics.LabelMap
|
|
|
|
|
packetsDroppedUnknown *expvar.Int // unknown dst pubkey
|
|
|
|
|
packetsDroppedFwdUnknown *expvar.Int // unknown dst pubkey on forward
|
|
|
|
|
packetsDroppedGone *expvar.Int // dst conn shutting down
|
|
|
|
|
packetsDroppedQueueHead *expvar.Int // queue full, drop head packet
|
|
|
|
|
packetsDroppedQueueTail *expvar.Int // queue full, drop tail packet
|
|
|
|
|
packetsDroppedWrite *expvar.Int // error writing to dst conn
|
|
|
|
|
_ [pad32bit]byte
|
|
|
|
|
packetsForwardedOut expvar.Int
|
|
|
|
|
packetsForwardedIn expvar.Int
|
|
|
|
|
peerGoneFrames expvar.Int // number of peer gone frames sent
|
|
|
|
|
accepts expvar.Int
|
|
|
|
|
curClients expvar.Int
|
|
|
|
|
curHomeClients expvar.Int // ones with preferred
|
|
|
|
|
clientsReplaced 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
|
|
|
|
|
multiForwarderCreated expvar.Int
|
|
|
|
|
multiForwarderDeleted expvar.Int
|
|
|
|
|
removePktForwardOther expvar.Int
|
|
|
|
|
avgQueueDuration *uint64 // In milliseconds; accessed atomically
|
|
|
|
|
_ [pad32bit]byte
|
|
|
|
|
packetsSent, bytesSent expvar.Int
|
|
|
|
|
packetsRecv, bytesRecv expvar.Int
|
|
|
|
|
packetsRecvByKind metrics.LabelMap
|
|
|
|
|
packetsRecvDisco *expvar.Int
|
|
|
|
|
packetsRecvOther *expvar.Int
|
|
|
|
|
_ [pad32bit]byte
|
|
|
|
|
packetsDropped expvar.Int
|
|
|
|
|
packetsDroppedReason metrics.LabelMap
|
|
|
|
|
packetsDroppedReasonCounters []*expvar.Int // indexed by dropReason
|
|
|
|
|
packetsDroppedType metrics.LabelMap
|
|
|
|
|
packetsDroppedTypeDisco *expvar.Int
|
|
|
|
|
packetsDroppedTypeOther *expvar.Int
|
|
|
|
|
_ [pad32bit]byte
|
|
|
|
|
packetsForwardedOut expvar.Int
|
|
|
|
|
packetsForwardedIn expvar.Int
|
|
|
|
|
peerGoneFrames expvar.Int // number of peer gone frames sent
|
|
|
|
|
accepts expvar.Int
|
|
|
|
|
curClients expvar.Int
|
|
|
|
|
curHomeClients expvar.Int // ones with preferred
|
|
|
|
|
clientsReplaced 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
|
|
|
|
|
multiForwarderCreated expvar.Int
|
|
|
|
|
multiForwarderDeleted expvar.Int
|
|
|
|
|
removePktForwardOther expvar.Int
|
|
|
|
|
avgQueueDuration *uint64 // In milliseconds; accessed atomically
|
|
|
|
|
|
|
|
|
|
// verifyClients only accepts client connections to the DERP server if the clientKey is a
|
|
|
|
|
// known peer in the network, as specified by a running tailscaled's client's local api.
|
|
|
|
@ -189,6 +187,7 @@ func NewServer(privateKey key.Private, logf logger.Logf) *Server {
|
|
|
|
|
limitedLogf: logger.RateLimitedFn(logf, 30*time.Second, 5, 100),
|
|
|
|
|
packetsRecvByKind: metrics.LabelMap{Label: "kind"},
|
|
|
|
|
packetsDroppedReason: metrics.LabelMap{Label: "reason"},
|
|
|
|
|
packetsDroppedType: metrics.LabelMap{Label: "type"},
|
|
|
|
|
clients: map[key.Public]*sclient{},
|
|
|
|
|
clientsEver: map[key.Public]bool{},
|
|
|
|
|
clientsMesh: map[key.Public]PacketForwarder{},
|
|
|
|
@ -202,12 +201,16 @@ func NewServer(privateKey key.Private, logf logger.Logf) *Server {
|
|
|
|
|
s.initMetacert()
|
|
|
|
|
s.packetsRecvDisco = s.packetsRecvByKind.Get("disco")
|
|
|
|
|
s.packetsRecvOther = s.packetsRecvByKind.Get("other")
|
|
|
|
|
s.packetsDroppedUnknown = s.packetsDroppedReason.Get("unknown_dest")
|
|
|
|
|
s.packetsDroppedFwdUnknown = s.packetsDroppedReason.Get("unknown_dest_on_fwd")
|
|
|
|
|
s.packetsDroppedGone = s.packetsDroppedReason.Get("gone")
|
|
|
|
|
s.packetsDroppedQueueHead = s.packetsDroppedReason.Get("queue_head")
|
|
|
|
|
s.packetsDroppedQueueTail = s.packetsDroppedReason.Get("queue_tail")
|
|
|
|
|
s.packetsDroppedWrite = s.packetsDroppedReason.Get("write_error")
|
|
|
|
|
s.packetsDroppedReasonCounters = []*expvar.Int{
|
|
|
|
|
s.packetsDroppedReason.Get("unknown_dest"),
|
|
|
|
|
s.packetsDroppedReason.Get("unknown_dest_on_fwd"),
|
|
|
|
|
s.packetsDroppedReason.Get("gone"),
|
|
|
|
|
s.packetsDroppedReason.Get("queue_head"),
|
|
|
|
|
s.packetsDroppedReason.Get("queue_tail"),
|
|
|
|
|
s.packetsDroppedReason.Get("write_error"),
|
|
|
|
|
}
|
|
|
|
|
s.packetsDroppedTypeDisco = s.packetsDroppedType.Get("disco")
|
|
|
|
|
s.packetsDroppedTypeOther = s.packetsDroppedType.Get("other")
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -631,11 +634,7 @@ func (c *sclient) handleFrameForwardPacket(ft frameType, fl uint32) error {
|
|
|
|
|
s.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
if dst == nil {
|
|
|
|
|
s.packetsDropped.Add(1)
|
|
|
|
|
s.packetsDroppedFwdUnknown.Add(1)
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping forwarded packet for unknown %x", dstKey)
|
|
|
|
|
}
|
|
|
|
|
s.recordDrop(contents, srcKey, dstKey, dropReasonUnknownDestOnFwd)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -686,11 +685,7 @@ func (c *sclient) handleFrameSendPacket(ft frameType, fl uint32) error {
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
s.packetsDropped.Add(1)
|
|
|
|
|
s.packetsDroppedUnknown.Add(1)
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping packet for unknown %x", dstKey)
|
|
|
|
|
}
|
|
|
|
|
s.recordDrop(contents, c.key, dstKey, dropReasonUnknownDest)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -702,6 +697,41 @@ func (c *sclient) handleFrameSendPacket(ft frameType, fl uint32) error {
|
|
|
|
|
return c.sendPkt(dst, p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dropReason is why we dropped a DERP frame.
|
|
|
|
|
type dropReason int
|
|
|
|
|
|
|
|
|
|
//go:generate stringer -type=dropReason -trimprefix=dropReason
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
dropReasonUnknownDest dropReason = iota // unknown destination pubkey
|
|
|
|
|
dropReasonUnknownDestOnFwd // unknown destination pubkey on a derp-forwarded packet
|
|
|
|
|
dropReasonGone // destination tailscaled disconnected before we could send
|
|
|
|
|
dropReasonQueueHead // destination queue is full, dropped packet at queue head
|
|
|
|
|
dropReasonQueueTail // destination queue is full, dropped packet at queue tail
|
|
|
|
|
dropReasonWriteError // OS write() failed
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (s *Server) recordDrop(packetBytes []byte, srcKey, dstKey key.Public, reason dropReason) {
|
|
|
|
|
s.packetsDropped.Add(1)
|
|
|
|
|
s.packetsDroppedReasonCounters[reason].Add(1)
|
|
|
|
|
if disco.LooksLikeDiscoWrapper(packetBytes) {
|
|
|
|
|
s.packetsDroppedTypeDisco.Add(1)
|
|
|
|
|
} else {
|
|
|
|
|
s.packetsDroppedTypeOther.Add(1)
|
|
|
|
|
}
|
|
|
|
|
if verboseDropKeys[dstKey] {
|
|
|
|
|
// Preformat the log string prior to calling limitedLogf. The
|
|
|
|
|
// limiter acts based on the format string, and we want to
|
|
|
|
|
// rate-limit per src/dst keys, not on the generic "dropped
|
|
|
|
|
// stuff" message.
|
|
|
|
|
msg := fmt.Sprintf("drop (%s) %s -> %s", srcKey.ShortString(), reason, dstKey.ShortString())
|
|
|
|
|
s.limitedLogf(msg)
|
|
|
|
|
}
|
|
|
|
|
if debug {
|
|
|
|
|
s.logf("dropping packet reason=%s dst=%s disco=%v", reason, dstKey, disco.LooksLikeDiscoWrapper(packetBytes))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *sclient) sendPkt(dst *sclient, p pkt) error {
|
|
|
|
|
s := c.s
|
|
|
|
|
dstKey := dst.key
|
|
|
|
@ -712,11 +742,7 @@ func (c *sclient) sendPkt(dst *sclient, p pkt) error {
|
|
|
|
|
for attempt := 0; attempt < 3; attempt++ {
|
|
|
|
|
select {
|
|
|
|
|
case <-dst.done:
|
|
|
|
|
s.packetsDropped.Add(1)
|
|
|
|
|
s.packetsDroppedGone.Add(1)
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping packet for shutdown client %x", dstKey)
|
|
|
|
|
}
|
|
|
|
|
s.recordDrop(p.bs, c.key, dstKey, dropReasonGone)
|
|
|
|
|
return nil
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
@ -728,35 +754,15 @@ func (c *sclient) sendPkt(dst *sclient, p pkt) error {
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case pkt := <-dst.sendQueue:
|
|
|
|
|
s.packetsDropped.Add(1)
|
|
|
|
|
s.packetsDroppedQueueHead.Add(1)
|
|
|
|
|
if verboseDropKeys[dstKey] {
|
|
|
|
|
// Generate a full string including src and dst, so
|
|
|
|
|
// the limiter kicks in once per src.
|
|
|
|
|
msg := fmt.Sprintf("tail drop %s -> %s", p.src.ShortString(), dstKey.ShortString())
|
|
|
|
|
c.s.limitedLogf(msg)
|
|
|
|
|
}
|
|
|
|
|
s.recordDrop(pkt.bs, c.key, dstKey, dropReasonQueueHead)
|
|
|
|
|
c.recordQueueTime(pkt.enqueuedAt)
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping packet from client %x queue head", dstKey)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Failed to make room for packet. This can happen in a heavily
|
|
|
|
|
// contended queue with racing writers. Give up and tail-drop in
|
|
|
|
|
// this case to keep reader unblocked.
|
|
|
|
|
s.packetsDropped.Add(1)
|
|
|
|
|
s.packetsDroppedQueueTail.Add(1)
|
|
|
|
|
if verboseDropKeys[dstKey] {
|
|
|
|
|
// Generate a full string including src and dst, so
|
|
|
|
|
// the limiter kicks in once per src.
|
|
|
|
|
msg := fmt.Sprintf("head drop %s -> %s", p.src.ShortString(), dstKey.ShortString())
|
|
|
|
|
c.s.limitedLogf(msg)
|
|
|
|
|
}
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping packet from client %x queue tail", dstKey)
|
|
|
|
|
}
|
|
|
|
|
s.recordDrop(p.bs, c.key, dstKey, dropReasonQueueTail)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -1031,12 +1037,8 @@ func (c *sclient) sendLoop(ctx context.Context) error {
|
|
|
|
|
// Drain the send queue to count dropped packets
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-c.sendQueue:
|
|
|
|
|
c.s.packetsDropped.Add(1)
|
|
|
|
|
c.s.packetsDroppedGone.Add(1)
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping packet for shutdown %x", c.key)
|
|
|
|
|
}
|
|
|
|
|
case pkt := <-c.sendQueue:
|
|
|
|
|
c.s.recordDrop(pkt.bs, pkt.src, c.key, dropReasonGone)
|
|
|
|
|
default:
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1179,11 +1181,7 @@ func (c *sclient) sendPacket(srcKey key.Public, contents []byte) (err error) {
|
|
|
|
|
defer func() {
|
|
|
|
|
// Stats update.
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.s.packetsDropped.Add(1)
|
|
|
|
|
c.s.packetsDroppedWrite.Add(1)
|
|
|
|
|
if debug {
|
|
|
|
|
c.logf("dropping packet to %x: %v", c.key, err)
|
|
|
|
|
}
|
|
|
|
|
c.s.recordDrop(contents, srcKey, c.key, dropReasonWriteError)
|
|
|
|
|
} else {
|
|
|
|
|
c.s.packetsSent.Add(1)
|
|
|
|
|
c.s.bytesSent.Add(int64(len(contents)))
|
|
|
|
|