309 lines
7.5 KiB
Go
309 lines
7.5 KiB
Go
package main
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/gammazero/deque"
|
|
)
|
|
|
|
const (
|
|
unknownProcessName = "<UNKNOWN>"
|
|
)
|
|
|
|
type Stat struct {
|
|
OpenSockets OpenSockets
|
|
Utilization Utilization
|
|
}
|
|
|
|
type ConnectionData struct {
|
|
DownloadBytes int
|
|
UploadBytes int
|
|
UploadPackets int
|
|
DownloadPackets int
|
|
ProcessName string
|
|
InterfaceName string
|
|
}
|
|
|
|
type NetworkData struct {
|
|
UploadBytes int
|
|
DownloadBytes int
|
|
UploadPackets int
|
|
DownloadPackets int
|
|
ConnCount int
|
|
}
|
|
|
|
func (d *NetworkData) DivideBy(n int) {
|
|
d.UploadBytes /= n
|
|
d.DownloadBytes /= n
|
|
d.UploadPackets /= n
|
|
d.DownloadPackets /= n
|
|
}
|
|
|
|
func (d *ConnectionData) DivideBy(n int) {
|
|
d.UploadBytes /= n
|
|
d.DownloadBytes /= n
|
|
d.UploadPackets /= n
|
|
d.DownloadPackets /= n
|
|
}
|
|
|
|
type ProcessesResult struct {
|
|
ProcessName string
|
|
Data *NetworkData
|
|
}
|
|
|
|
type RemoteAddrsResult struct {
|
|
Addr string
|
|
Data *NetworkData
|
|
}
|
|
|
|
type ConnectionsResult struct {
|
|
Conn Connection
|
|
Data *ConnectionData
|
|
}
|
|
|
|
type Snapshot struct {
|
|
Processes map[string]*NetworkData
|
|
RemoteAddrs map[string]*NetworkData
|
|
Connections map[Connection]*ConnectionData
|
|
TotalUploadBytes int
|
|
TotalDownloadBytes int
|
|
TotalUploadPackets int
|
|
TotalDownloadPackets int
|
|
TotalConnections int
|
|
}
|
|
|
|
func (s *Snapshot) TopNProcesses(n int, mode ViewMode) []ProcessesResult {
|
|
var items []ProcessesResult
|
|
for k, v := range s.Processes {
|
|
items = append(items, ProcessesResult{ProcessName: k, Data: v})
|
|
}
|
|
|
|
switch mode {
|
|
case ModeTableBytes:
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Data.DownloadBytes+items[i].Data.UploadBytes > items[j].Data.DownloadBytes+items[j].Data.UploadBytes
|
|
})
|
|
case ModeTablePackets:
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Data.DownloadPackets+items[i].Data.UploadPackets > items[j].Data.DownloadPackets+items[j].Data.UploadPackets
|
|
})
|
|
}
|
|
|
|
if len(items) < n {
|
|
n = len(items)
|
|
}
|
|
return items[:n]
|
|
}
|
|
|
|
func (s *Snapshot) TopNRemoteAddrs(n int, mode ViewMode) []RemoteAddrsResult {
|
|
var items []RemoteAddrsResult
|
|
for k, v := range s.RemoteAddrs {
|
|
items = append(items, RemoteAddrsResult{Addr: k, Data: v})
|
|
}
|
|
|
|
switch mode {
|
|
case ModeTableBytes:
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Data.DownloadBytes+items[i].Data.UploadBytes > items[j].Data.DownloadBytes+items[j].Data.UploadBytes
|
|
})
|
|
case ModeTablePackets:
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Data.DownloadPackets+items[i].Data.UploadPackets > items[j].Data.DownloadPackets+items[j].Data.UploadPackets
|
|
})
|
|
}
|
|
|
|
if len(items) < n {
|
|
n = len(items)
|
|
}
|
|
return items[:n]
|
|
}
|
|
|
|
func (s *Snapshot) TopNConnections(n int, mode ViewMode) []ConnectionsResult {
|
|
var items []ConnectionsResult
|
|
for k, v := range s.Connections {
|
|
items = append(items, ConnectionsResult{Conn: k, Data: v})
|
|
}
|
|
|
|
switch mode {
|
|
case ModeTableBytes:
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Data.DownloadBytes+items[i].Data.UploadBytes > items[j].Data.DownloadBytes+items[j].Data.UploadBytes
|
|
})
|
|
case ModeTablePackets:
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Data.DownloadPackets+items[i].Data.UploadPackets > items[j].Data.DownloadPackets+items[j].Data.UploadPackets
|
|
})
|
|
}
|
|
|
|
if len(items) < n {
|
|
n = len(items)
|
|
}
|
|
return items[:n]
|
|
}
|
|
|
|
type StatsManager struct {
|
|
mut sync.Mutex
|
|
ring *deque.Deque
|
|
ratio int
|
|
mode ViewMode
|
|
}
|
|
|
|
func NewStatsManager(opt Options) *StatsManager {
|
|
return &StatsManager{
|
|
ring: deque.New(),
|
|
ratio: opt.Interval,
|
|
mode: opt.ViewMode,
|
|
}
|
|
}
|
|
|
|
func (s *StatsManager) Put(stat Stat) {
|
|
s.mut.Lock()
|
|
defer s.mut.Unlock()
|
|
|
|
const maxsize = 3
|
|
if s.ring.Len() >= maxsize {
|
|
s.ring.PopFront()
|
|
}
|
|
s.ring.PushBack(stat)
|
|
}
|
|
|
|
func (s *StatsManager) getProcName(openSockets OpenSockets, localSocket LocalSocket) string {
|
|
ips := []string{localSocket.IP, "*"}
|
|
for _, ip := range ips {
|
|
cloned := localSocket
|
|
cloned.IP = ip
|
|
|
|
v, ok := openSockets[cloned]
|
|
if ok {
|
|
return v.String()
|
|
}
|
|
}
|
|
return unknownProcessName
|
|
}
|
|
|
|
func (s *StatsManager) GetStats() interface{} {
|
|
s.mut.Lock()
|
|
defer s.mut.Unlock()
|
|
|
|
size := s.ring.Len()
|
|
if size <= 0 {
|
|
return nil
|
|
}
|
|
|
|
if s.mode == ModePlotProcesses {
|
|
return s.getNetworkData(size)
|
|
}
|
|
return s.getSnapshot(size)
|
|
}
|
|
|
|
func (s *StatsManager) getNetworkData(size int) *NetworkData {
|
|
visited := map[Connection]bool{}
|
|
var uploadBytes, downloadBytes, uploadPackets, downloadPackets, connections int
|
|
|
|
for i := 0; i < size; i++ {
|
|
stat := s.ring.At(i).(Stat)
|
|
for conn, info := range stat.Utilization {
|
|
procName := s.getProcName(stat.OpenSockets, conn.Local)
|
|
if procName == unknownProcessName {
|
|
continue
|
|
}
|
|
|
|
if !visited[conn] {
|
|
connections++
|
|
visited[conn] = true
|
|
}
|
|
|
|
uploadBytes += info.UploadBytes
|
|
downloadBytes += info.DownloadBytes
|
|
uploadPackets += info.UploadPackets
|
|
downloadPackets += info.DownloadPackets
|
|
}
|
|
}
|
|
|
|
size = size * s.ratio
|
|
return &NetworkData{
|
|
UploadBytes: uploadBytes / size,
|
|
DownloadBytes: downloadBytes / size,
|
|
UploadPackets: uploadPackets / size,
|
|
DownloadPackets: downloadPackets / size,
|
|
ConnCount: connections,
|
|
}
|
|
}
|
|
|
|
func (s *StatsManager) getSnapshot(size int) *Snapshot {
|
|
processes := map[string]*NetworkData{}
|
|
remoteAddr := map[string]*NetworkData{}
|
|
connections := map[Connection]*ConnectionData{}
|
|
visited := map[Connection]bool{}
|
|
var totalUploadBytes, totalDownloadBytes, totalUploadPackets, totalDownloadPackets, totalConnections int
|
|
|
|
for i := 0; i < size; i++ {
|
|
stat := s.ring.At(i).(Stat)
|
|
for conn, info := range stat.Utilization {
|
|
procName := s.getProcName(stat.OpenSockets, conn.Local)
|
|
if _, ok := connections[conn]; !ok {
|
|
connections[conn] = &ConnectionData{
|
|
InterfaceName: info.Interface,
|
|
ProcessName: procName,
|
|
}
|
|
}
|
|
connections[conn].UploadBytes += info.UploadBytes
|
|
connections[conn].DownloadBytes += info.DownloadBytes
|
|
connections[conn].UploadPackets += info.UploadPackets
|
|
connections[conn].DownloadPackets += info.DownloadPackets
|
|
|
|
if _, ok := remoteAddr[conn.Remote.IP]; !ok {
|
|
remoteAddr[conn.Remote.IP] = &NetworkData{}
|
|
}
|
|
if !visited[conn] {
|
|
totalConnections++
|
|
remoteAddr[conn.Remote.IP].ConnCount++
|
|
}
|
|
remoteAddr[conn.Remote.IP].UploadBytes += info.UploadBytes
|
|
remoteAddr[conn.Remote.IP].DownloadBytes += info.UploadBytes
|
|
remoteAddr[conn.Remote.IP].UploadPackets += info.UploadPackets
|
|
remoteAddr[conn.Remote.IP].DownloadPackets += info.DownloadPackets
|
|
|
|
if _, ok := processes[procName]; !ok {
|
|
processes[procName] = &NetworkData{}
|
|
}
|
|
if !visited[conn] {
|
|
processes[procName].ConnCount++
|
|
}
|
|
processes[procName].UploadBytes += info.UploadBytes
|
|
processes[procName].DownloadBytes += info.DownloadBytes
|
|
processes[procName].UploadPackets += info.UploadPackets
|
|
processes[procName].DownloadPackets += info.DownloadPackets
|
|
|
|
totalUploadPackets += info.UploadPackets
|
|
totalDownloadPackets += info.DownloadPackets
|
|
totalUploadBytes += info.UploadBytes
|
|
totalDownloadBytes += info.DownloadBytes
|
|
visited[conn] = true
|
|
}
|
|
}
|
|
|
|
size = size * s.ratio
|
|
for _, v := range processes {
|
|
v.DivideBy(size)
|
|
}
|
|
for _, v := range remoteAddr {
|
|
v.DivideBy(size)
|
|
}
|
|
for _, v := range connections {
|
|
v.DivideBy(size)
|
|
}
|
|
|
|
return &Snapshot{
|
|
Processes: processes,
|
|
RemoteAddrs: remoteAddr,
|
|
Connections: connections,
|
|
TotalUploadBytes: totalUploadBytes / size,
|
|
TotalDownloadBytes: totalDownloadBytes / size,
|
|
TotalUploadPackets: totalUploadPackets / size,
|
|
TotalDownloadPackets: totalDownloadPackets / size,
|
|
TotalConnections: totalConnections,
|
|
}
|
|
}
|