-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Expand file tree
/
Copy pathnetlogtype.go
More file actions
157 lines (130 loc) · 5.08 KB
/
netlogtype.go
File metadata and controls
157 lines (130 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package netlogtype defines types for network logging.
package netlogtype
import (
"maps"
"net/netip"
"sync"
"time"
"tailscale.com/tailcfg"
"tailscale.com/types/ipproto"
)
// Message is the log message that captures network traffic.
type Message struct {
NodeID tailcfg.StableNodeID `json:"nodeId"` // e.g., "n123456CNTRL"
Start time.Time `json:"start"` // inclusive
End time.Time `json:"end"` // inclusive
SrcNode Node `json:"srcNode,omitzero"`
DstNodes []Node `json:"dstNodes,omitempty"`
VirtualTraffic []ConnectionCounts `json:"virtualTraffic,omitempty"`
SubnetTraffic []ConnectionCounts `json:"subnetTraffic,omitempty"`
ExitTraffic []ConnectionCounts `json:"exitTraffic,omitempty"`
PhysicalTraffic []ConnectionCounts `json:"physicalTraffic,omitempty"`
}
const (
messageJSON = `{"nodeId":` + maxJSONStableID + `,` + minJSONNodes + `,` + maxJSONTimeRange + `,` + minJSONTraffic + `}`
maxJSONStableID = `"n0123456789abcdefCNTRL"`
minJSONNodes = `"srcNode":{},"dstNodes":[]`
maxJSONTimeRange = `"start":` + maxJSONRFC3339 + `,"end":` + maxJSONRFC3339
maxJSONRFC3339 = `"0001-01-01T00:00:00.000000000Z"`
minJSONTraffic = `"virtualTraffic":{},"subnetTraffic":{},"exitTraffic":{},"physicalTraffic":{}`
// MinMessageJSONSize is the overhead size of Message when it is
// serialized as JSON assuming that each field is minimally populated.
// Each [Node] occupies at least [MinNodeJSONSize].
// Each [ConnectionCounts] occupies at most [MaxConnectionCountsJSONSize].
MinMessageJSONSize = len(messageJSON)
maxJSONConnCounts = `{` + maxJSONConn + `,` + maxJSONCounts + `}`
maxJSONConn = `"proto":` + maxJSONProto + `,"src":` + maxJSONAddrPort + `,"dst":` + maxJSONAddrPort
maxJSONProto = `255`
maxJSONAddrPort = `"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535"`
maxJSONCounts = `"txPkts":` + maxJSONCount + `,"txBytes":` + maxJSONCount + `,"rxPkts":` + maxJSONCount + `,"rxBytes":` + maxJSONCount
maxJSONCount = `18446744073709551615`
// MaxConnectionCountsJSONSize is the maximum size of a ConnectionCounts
// when it is serialized as JSON, assuming no superfluous whitespace.
// It does not include the trailing comma that often appears when
// this object is nested within an array.
// It assumes that netip.Addr never has IPv6 zones.
MaxConnectionCountsJSONSize = len(maxJSONConnCounts)
)
// Node is information about a node.
type Node struct {
// NodeID is the stable ID of the node.
NodeID tailcfg.StableNodeID `json:"nodeId"`
// Name is the fully-qualified name of the node.
Name string `json:"name,omitzero"` // e.g., "carbonite.example.ts.net"
// Addresses are the Tailscale IP addresses of the node.
Addresses []netip.Addr `json:"addresses,omitempty"`
// OS is the operating system of the node.
OS string `json:"os,omitzero"` // e.g., "linux"
// User is the user that owns the node.
// It is not populated if the node is tagged.
User string `json:"user,omitzero"` // e.g., "[email protected]"
// Tags are the tags of the node.
// It is not populated if the node is owned by a user.
Tags []string `json:"tags,omitempty"` // e.g., ["tag:prod","tag:logs"]
}
// ConnectionCounts is a flattened struct of both a connection and counts.
type ConnectionCounts struct {
Connection
Counts
}
// Connection is a 5-tuple of proto, source and destination IP and port.
type Connection struct {
Proto ipproto.Proto `json:"proto,omitzero"`
Src netip.AddrPort `json:"src,omitzero"`
Dst netip.AddrPort `json:"dst,omitzero"`
}
func (c Connection) IsZero() bool { return c == Connection{} }
// Counts are statistics about a particular connection.
type Counts struct {
TxPackets uint64 `json:"txPkts,omitzero"`
TxBytes uint64 `json:"txBytes,omitzero"`
RxPackets uint64 `json:"rxPkts,omitzero"`
RxBytes uint64 `json:"rxBytes,omitzero"`
}
func (c Counts) IsZero() bool { return c == Counts{} }
// Add adds the counts from both c1 and c2.
func (c1 Counts) Add(c2 Counts) Counts {
c1.TxPackets += c2.TxPackets
c1.TxBytes += c2.TxBytes
c1.RxPackets += c2.RxPackets
c1.RxBytes += c2.RxBytes
return c1
}
// CountsByConnection is a count of packets and bytes for each connection.
// All methods are safe for concurrent calls.
type CountsByConnection struct {
mu sync.Mutex
m map[Connection]Counts
}
// Add adds packets and bytes for the specified connection.
func (c *CountsByConnection) Add(proto ipproto.Proto, src, dst netip.AddrPort, packets, bytes int, recv bool) {
conn := Connection{Proto: proto, Src: src, Dst: dst}
c.mu.Lock()
defer c.mu.Unlock()
if c.m == nil {
c.m = make(map[Connection]Counts)
}
cnts := c.m[conn]
if recv {
cnts.RxPackets += uint64(packets)
cnts.RxBytes += uint64(bytes)
} else {
cnts.TxPackets += uint64(packets)
cnts.TxBytes += uint64(bytes)
}
c.m[conn] = cnts
}
// Clone deep copies the map.
func (c *CountsByConnection) Clone() map[Connection]Counts {
c.mu.Lock()
defer c.mu.Unlock()
return maps.Clone(c.m)
}
// Reset clear the map.
func (c *CountsByConnection) Reset() {
c.mu.Lock()
defer c.mu.Unlock()
clear(c.m)
}