Report FBU metrics as untracked for service-mode proxied sessions instead of as zero

This commit is contained in:
Viktor Liu
2026-09-23 08:51:31 +02:00
parent e0fe7cccb7
commit adcd8e3ec5
6 changed files with 45 additions and 13 deletions
+1
View File
@@ -153,6 +153,7 @@ func (e *Engine) startVNCServer(authConfig *sshauth.Config) error {
MaxFBURects: t.MaxFBURects,
MaxWriteBytes: t.MaxWriteBytes,
WriteNanos: t.WriteNanos,
FBUsTracked: t.FBUsTracked,
})
}
}
+17 -11
View File
@@ -115,20 +115,26 @@ func (m *influxDBMetrics) RecordVNCSessionTick(_ context.Context, agentInfo Agen
m.mu.Lock()
defer m.mu.Unlock()
fields := map[string]float64{
"period_seconds": tick.Period.Seconds(),
"bytes_out": float64(tick.BytesOut),
"writes": float64(tick.Writes),
"max_write_bytes": float64(tick.MaxWriteBytes),
"write_time_seconds": float64(tick.WriteNanos) / 1e9,
}
// Left out rather than written as zero when they were not observed, so a
// service-mode session does not read as one that sent no updates.
if tick.FBUsTracked {
fields["fbus"] = float64(tick.FBUs)
fields["max_fbu_bytes"] = float64(tick.MaxFBUBytes)
fields["max_fbu_rects"] = float64(tick.MaxFBURects)
}
m.samples = append(m.samples, influxSample{
measurement: "netbird_vnc_traffic",
tags: tags,
fields: map[string]float64{
"period_seconds": tick.Period.Seconds(),
"bytes_out": float64(tick.BytesOut),
"writes": float64(tick.Writes),
"fbus": float64(tick.FBUs),
"max_fbu_bytes": float64(tick.MaxFBUBytes),
"max_fbu_rects": float64(tick.MaxFBURects),
"max_write_bytes": float64(tick.MaxWriteBytes),
"write_time_seconds": float64(tick.WriteNanos) / 1e9,
},
timestamp: time.Now(),
fields: fields,
timestamp: time.Now(),
})
m.trimLocked()
}
+3
View File
@@ -100,6 +100,9 @@ type VNCSessionTick struct {
MaxFBURects uint64
MaxWriteBytes uint64
WriteNanos uint64
// FBUsTracked is false when the FBU fields could not be observed (a
// service-mode proxy). They are then unknown rather than zero.
FBUsTracked bool
}
// ConnectionStageTimestamps holds timestamps for each connection stage
+22
View File
@@ -23,6 +23,11 @@ type SessionTick struct {
MaxFBURects uint64
MaxWriteBytes uint64
WriteNanos uint64
// FBUsTracked is false when the connection carried framebuffer updates it
// could not see the boundaries of, which is the case for a service-mode
// proxy relaying bytes to a per-session agent. FBUs, MaxFBUBytes and
// MaxFBURects then mean "unknown", not "none".
FBUsTracked bool
}
// sessionTickInterval is how often metricsConn emits a SessionTick. One
@@ -40,6 +45,9 @@ type metricsConn struct {
net.Conn
recorder func(SessionTick)
// framed is true when the session writing through this connection marks
// its FramebufferUpdate boundaries with beginFBU/endFBU.
framed bool
bytesOut atomic.Uint64
writes atomic.Uint64
@@ -72,9 +80,22 @@ type metricsConn struct {
}
func newMetricsConn(c net.Conn, recorder func(SessionTick)) net.Conn {
return newMetricsConnFramed(c, recorder, true)
}
// newProxyMetricsConn wraps a connection whose bytes are relayed to a session
// running elsewhere, the service-mode agent. Nothing on this side sees where
// one FramebufferUpdate ends and the next begins, so its ticks carry byte and
// write counts only and report the FBU fields as untracked.
func newProxyMetricsConn(c net.Conn, recorder func(SessionTick)) net.Conn {
return newMetricsConnFramed(c, recorder, false)
}
func newMetricsConnFramed(c net.Conn, recorder func(SessionTick), framed bool) net.Conn {
m := &metricsConn{
Conn: c,
recorder: recorder,
framed: framed,
tickStart: time.Now(),
done: make(chan struct{}),
}
@@ -137,6 +158,7 @@ func (m *metricsConn) flushTick(final bool) {
MaxFBURects: maxRects,
MaxWriteBytes: maxPkt,
WriteNanos: dns,
FBUsTracked: m.framed,
})
}
+1 -1
View File
@@ -57,7 +57,7 @@ func (s *Server) serviceAcceptLoop(ln net.Listener) {
continue
}
enableTCPKeepAlive(conn, s.log)
metered := newMetricsConn(conn, s.sessionRecorder)
metered := newProxyMetricsConn(conn, s.sessionRecorder)
s.retrackConn(conn, metered)
if !s.beginHandler() {
s.releaseConnSlot()
+1 -1
View File
@@ -416,7 +416,7 @@ func (s *Server) serviceAcceptLoop(ln net.Listener) {
continue
}
enableTCPKeepAlive(conn, s.log)
metered := newMetricsConn(conn, s.sessionRecorder)
metered := newProxyMetricsConn(conn, s.sessionRecorder)
s.retrackConn(conn, metered)
if !s.beginHandler() {
s.releaseConnSlot()