17
17
package collector
18
18
19
19
import (
20
+ "encoding/binary"
20
21
"errors"
21
22
"fmt"
22
23
"log/slog"
59
60
ipv6ForwardTotal = "bsdNetstatIPv6ForwardTotal"
60
61
ipv6DeliveredTotal = "bsdNetstatIPv6DeliveredTotal"
61
62
63
+ tcpStates = []string {
64
+ "CLOSED" , "LISTEN" , "SYN_SENT" , "SYN_RCVD" ,
65
+ "ESTABLISHED" , "CLOSE_WAIT" , "FIN_WAIT_1" , "CLOSING" ,
66
+ "LAST_ACK" , "FIN_WAIT_2" , "TIME_WAIT" ,
67
+ }
68
+
69
+ tcpStatesMetric = prometheus .NewDesc (
70
+ prometheus .BuildFQName (namespace , "netstat" , "tcp_connections" ),
71
+ "Number of TCP connections per state" , []string {"state" }, nil )
72
+
62
73
counterMetrics = map [string ]* prometheus.Desc {
63
74
// TCP stats
64
75
tcpSendTotal : prometheus .NewDesc (
@@ -242,6 +253,30 @@ func getData(queryString string, expectedSize int) ([]byte, error) {
242
253
return data , nil
243
254
}
244
255
256
+ func getTCPStates () ([]uint64 , error ) {
257
+
258
+ // This sysctl returns an array of uint64
259
+ data , err := sysctlRaw ("net.inet.tcp.states" )
260
+
261
+ if err != nil {
262
+ return nil , err
263
+ }
264
+
265
+ if len (data )/ 8 != len (tcpStates ) {
266
+ return nil , fmt .Errorf ("invalid TCP states data: expected %d entries, found %d" , len (tcpStates ), len (data )/ 8 )
267
+ }
268
+
269
+ states := make ([]uint64 , 0 )
270
+
271
+ offset := 0
272
+ for range len (tcpStates ) {
273
+ s := data [offset : offset + 8 ]
274
+ offset += 8
275
+ states = append (states , binary .NativeEndian .Uint64 (s ))
276
+ }
277
+ return states , nil
278
+ }
279
+
245
280
type netStatCollector struct {
246
281
netStatMetric * prometheus.Desc
247
282
}
@@ -309,6 +344,15 @@ func (c *netStatCollector) Update(ch chan<- prometheus.Metric) error {
309
344
)
310
345
}
311
346
347
+ tcpConnsPerStates , err := getTCPStates ()
348
+
349
+ if err != nil {
350
+ return err
351
+ }
352
+
353
+ for i , value := range tcpConnsPerStates {
354
+ ch <- prometheus .MustNewConstMetric (tcpStatesMetric , prometheus .GaugeValue , float64 (value ), tcpStates [i ])
355
+ }
312
356
return nil
313
357
}
314
358
@@ -323,6 +367,16 @@ func getFreeBSDDataMock(sysctl string) []byte {
323
367
size := int (unsafe .Sizeof (C.struct_tcpstat {}))
324
368
325
369
return unsafe .Slice ((* byte )(unsafe .Pointer (& tcpStats )), size )
370
+ } else if sysctl == "net.inet.tcp.states" {
371
+ tcpStatesSlice := make ([]byte , 0 , len (tcpStates )* 8 )
372
+ tcpStatesValues := []uint64 {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 }
373
+
374
+ for _ , value := range tcpStatesValues {
375
+ tcpStatesSlice = binary .NativeEndian .AppendUint64 (tcpStatesSlice , value )
376
+ }
377
+
378
+ return tcpStatesSlice
379
+
326
380
} else if sysctl == "net.inet.udp.stats" {
327
381
udpStats := C.struct_udpstat {
328
382
udps_opackets : 1234 ,
0 commit comments