mirror of
				https://github.com/prometheus/node_exporter.git
				synced 2025-08-20 18:33:52 -07:00 
			
		
		
		
	netstat_freebsd: add support for TCP state metrics
Metric added (gauge):
node_netstat_tcp_connections{state="CLOSED"}
node_netstat_tcp_connections{state="CLOSE_WAIT"}
node_netstat_tcp_connections{state="CLOSING"}
node_netstat_tcp_connections{state="ESTABLISHED"}
node_netstat_tcp_connections{state="FIN_WAIT_1"}
node_netstat_tcp_connections{state="FIN_WAIT_2"}
node_netstat_tcp_connections{state="LAST_ACK"}
node_netstat_tcp_connections{state="LISTEN"}
node_netstat_tcp_connections{state="SYN_RCVD"}
node_netstat_tcp_connections{state="SYN_SENT"}
node_netstat_tcp_connections{state="TIME_WAIT"}
Signed-off-by: Danilo Egea Gondolfo <danilo@FreeBSD.org>
			
			
This commit is contained in:
		
							parent
							
								
									2db27ff2fa
								
							
						
					
					
						commit
						4bb3f0315d
					
				| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
package collector
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +60,16 @@ var (
 | 
			
		|||
	ipv6ForwardTotal       = "bsdNetstatIPv6ForwardTotal"
 | 
			
		||||
	ipv6DeliveredTotal     = "bsdNetstatIPv6DeliveredTotal"
 | 
			
		||||
 | 
			
		||||
	tcpStates = []string{
 | 
			
		||||
		"CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD",
 | 
			
		||||
		"ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
 | 
			
		||||
		"LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tcpStatesMetric = prometheus.NewDesc(
 | 
			
		||||
		prometheus.BuildFQName(namespace, "netstat", "tcp_connections"),
 | 
			
		||||
		"Number of TCP connections per state", []string{"state"}, nil)
 | 
			
		||||
 | 
			
		||||
	counterMetrics = map[string]*prometheus.Desc{
 | 
			
		||||
		// TCP stats
 | 
			
		||||
		tcpSendTotal: prometheus.NewDesc(
 | 
			
		||||
| 
						 | 
				
			
			@ -242,6 +253,30 @@ func getData(queryString string, expectedSize int) ([]byte, error) {
 | 
			
		|||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getTCPStates() ([]uint64, error) {
 | 
			
		||||
 | 
			
		||||
	// This sysctl returns an array of uint64
 | 
			
		||||
	data, err := sysctlRaw("net.inet.tcp.states")
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(data)/8 != len(tcpStates) {
 | 
			
		||||
		return nil, fmt.Errorf("invalid TCP states data: expected %d entries, found %d", len(tcpStates), len(data)/8)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	states := make([]uint64, 0)
 | 
			
		||||
 | 
			
		||||
	offset := 0
 | 
			
		||||
	for range len(tcpStates) {
 | 
			
		||||
		s := data[offset : offset+8]
 | 
			
		||||
		offset += 8
 | 
			
		||||
		states = append(states, binary.NativeEndian.Uint64(s))
 | 
			
		||||
	}
 | 
			
		||||
	return states, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type netStatCollector struct {
 | 
			
		||||
	netStatMetric *prometheus.Desc
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -309,6 +344,15 @@ func (c *netStatCollector) Update(ch chan<- prometheus.Metric) error {
 | 
			
		|||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tcpConnsPerStates, err := getTCPStates()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, value := range tcpConnsPerStates {
 | 
			
		||||
		ch <- prometheus.MustNewConstMetric(tcpStatesMetric, prometheus.GaugeValue, float64(value), tcpStates[i])
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -323,6 +367,16 @@ func getFreeBSDDataMock(sysctl string) []byte {
 | 
			
		|||
		size := int(unsafe.Sizeof(C.struct_tcpstat{}))
 | 
			
		||||
 | 
			
		||||
		return unsafe.Slice((*byte)(unsafe.Pointer(&tcpStats)), size)
 | 
			
		||||
	} else if sysctl == "net.inet.tcp.states" {
 | 
			
		||||
		tcpStatesSlice := make([]byte, 0, len(tcpStates)*8)
 | 
			
		||||
		tcpStatesValues := []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
 | 
			
		||||
 | 
			
		||||
		for _, value := range tcpStatesValues {
 | 
			
		||||
			tcpStatesSlice = binary.NativeEndian.AppendUint64(tcpStatesSlice, value)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return tcpStatesSlice
 | 
			
		||||
 | 
			
		||||
	} else if sysctl == "net.inet.udp.stats" {
 | 
			
		||||
		udpStats := C.struct_udpstat{
 | 
			
		||||
			udps_opackets: 1234,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,6 +62,24 @@ func TestGetTCPMetrics(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetTCPStatesMetrics(t *testing.T) {
 | 
			
		||||
	testSetup()
 | 
			
		||||
 | 
			
		||||
	tcpData, err := getTCPStates()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("unexpected error:", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
 | 
			
		||||
 | 
			
		||||
	for i, value := range tcpData {
 | 
			
		||||
		if got, want := float64(value), float64(expected[i]); got != want {
 | 
			
		||||
			t.Errorf("unexpected %s value: want %f, got %f", tcpStates[i], want, got)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetUDPMetrics(t *testing.T) {
 | 
			
		||||
	testSetup()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -143,20 +161,24 @@ func TestGetIPv6Metrics(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestNetStatCollectorUpdate(t *testing.T) {
 | 
			
		||||
	ch := make(chan prometheus.Metric, len(counterMetrics))
 | 
			
		||||
	collector := &netStatCollector{
 | 
			
		||||
		netStatMetric: prometheus.NewDesc("netstat_metric", "NetStat Metric", nil, nil),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	totalMetrics := len(counterMetrics) + len(tcpStates)
 | 
			
		||||
 | 
			
		||||
	ch := make(chan prometheus.Metric, totalMetrics)
 | 
			
		||||
 | 
			
		||||
	err := collector.Update(ch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("unexpected error:", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if got, want := len(ch), len(counterMetrics); got != want {
 | 
			
		||||
		t.Errorf("metric count mismatch: want %d, got %d", want, got)
 | 
			
		||||
	if got, want := len(ch), totalMetrics; got != want {
 | 
			
		||||
		t.Fatalf("metric count mismatch: want %d, got %d", want, got)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for range len(counterMetrics) {
 | 
			
		||||
	for range totalMetrics {
 | 
			
		||||
		<-ch
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue