110 lines
3.4 KiB
Go
110 lines
3.4 KiB
Go
|
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
// Package connstats maintains statistics about connections
|
||
|
// flowing through a TUN device (which operate at the IP layer).
|
||
|
package connstats
|
||
|
|
||
|
import (
|
||
|
"net/netip"
|
||
|
"sync"
|
||
|
|
||
|
"tailscale.com/net/packet"
|
||
|
"tailscale.com/types/netlogtype"
|
||
|
)
|
||
|
|
||
|
// Statistics maintains counters for every connection.
|
||
|
// All methods are safe for concurrent use.
|
||
|
// The zero value is ready for use.
|
||
|
type Statistics struct {
|
||
|
mu sync.Mutex
|
||
|
virtual map[netlogtype.Connection]netlogtype.Counts
|
||
|
physical map[netlogtype.Connection]netlogtype.Counts
|
||
|
}
|
||
|
|
||
|
// UpdateTxVirtual updates the counters for a transmitted IP packet
|
||
|
// The source and destination of the packet directly correspond with
|
||
|
// the source and destination in netlogtype.Connection.
|
||
|
func (s *Statistics) UpdateTxVirtual(b []byte) {
|
||
|
s.updateVirtual(b, false)
|
||
|
}
|
||
|
|
||
|
// UpdateRxVirtual updates the counters for a received IP packet.
|
||
|
// The source and destination of the packet are inverted with respect to
|
||
|
// the source and destination in netlogtype.Connection.
|
||
|
func (s *Statistics) UpdateRxVirtual(b []byte) {
|
||
|
s.updateVirtual(b, true)
|
||
|
}
|
||
|
|
||
|
func (s *Statistics) updateVirtual(b []byte, receive bool) {
|
||
|
var p packet.Parsed
|
||
|
p.Decode(b)
|
||
|
conn := netlogtype.Connection{Proto: p.IPProto, Src: p.Src, Dst: p.Dst}
|
||
|
if receive {
|
||
|
conn.Src, conn.Dst = conn.Dst, conn.Src
|
||
|
}
|
||
|
|
||
|
s.mu.Lock()
|
||
|
defer s.mu.Unlock()
|
||
|
if s.virtual == nil {
|
||
|
s.virtual = make(map[netlogtype.Connection]netlogtype.Counts)
|
||
|
}
|
||
|
cnts := s.virtual[conn]
|
||
|
if receive {
|
||
|
cnts.RxPackets++
|
||
|
cnts.RxBytes += uint64(len(b))
|
||
|
} else {
|
||
|
cnts.TxPackets++
|
||
|
cnts.TxBytes += uint64(len(b))
|
||
|
}
|
||
|
s.virtual[conn] = cnts
|
||
|
}
|
||
|
|
||
|
// UpdateTxPhysical updates the counters for a transmitted wireguard packet
|
||
|
// The src is always a Tailscale IP address, representing some remote peer.
|
||
|
// The dst is a remote IP address and port that corresponds
|
||
|
// with some physical peer backing the Tailscale IP address.
|
||
|
func (s *Statistics) UpdateTxPhysical(src netip.Addr, dst netip.AddrPort, n int) {
|
||
|
s.updatePhysical(src, dst, n, false)
|
||
|
}
|
||
|
|
||
|
// UpdateRxPhysical updates the counters for a received wireguard packet.
|
||
|
// The src is always a Tailscale IP address, representing some remote peer.
|
||
|
// The dst is a remote IP address and port that corresponds
|
||
|
// with some physical peer backing the Tailscale IP address.
|
||
|
func (s *Statistics) UpdateRxPhysical(src netip.Addr, dst netip.AddrPort, n int) {
|
||
|
s.updatePhysical(src, dst, n, true)
|
||
|
}
|
||
|
|
||
|
func (s *Statistics) updatePhysical(src netip.Addr, dst netip.AddrPort, n int, receive bool) {
|
||
|
conn := netlogtype.Connection{Src: netip.AddrPortFrom(src, 0), Dst: dst}
|
||
|
|
||
|
s.mu.Lock()
|
||
|
defer s.mu.Unlock()
|
||
|
if s.physical == nil {
|
||
|
s.physical = make(map[netlogtype.Connection]netlogtype.Counts)
|
||
|
}
|
||
|
cnts := s.physical[conn]
|
||
|
if receive {
|
||
|
cnts.RxPackets++
|
||
|
cnts.RxBytes += uint64(n)
|
||
|
} else {
|
||
|
cnts.TxPackets++
|
||
|
cnts.TxBytes += uint64(n)
|
||
|
}
|
||
|
s.physical[conn] = cnts
|
||
|
}
|
||
|
|
||
|
// Extract extracts and resets the counters for all active connections.
|
||
|
// It must be called periodically otherwise the memory used is unbounded.
|
||
|
func (s *Statistics) Extract() (virtual, physical map[netlogtype.Connection]netlogtype.Counts) {
|
||
|
s.mu.Lock()
|
||
|
defer s.mu.Unlock()
|
||
|
virtual = s.virtual
|
||
|
s.virtual = make(map[netlogtype.Connection]netlogtype.Counts)
|
||
|
physical = s.physical
|
||
|
s.physical = make(map[netlogtype.Connection]netlogtype.Counts)
|
||
|
return virtual, physical
|
||
|
}
|