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
 | 
					package collector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log/slog"
 | 
						"log/slog"
 | 
				
			||||||
| 
						 | 
					@ -59,6 +60,16 @@ var (
 | 
				
			||||||
	ipv6ForwardTotal       = "bsdNetstatIPv6ForwardTotal"
 | 
						ipv6ForwardTotal       = "bsdNetstatIPv6ForwardTotal"
 | 
				
			||||||
	ipv6DeliveredTotal     = "bsdNetstatIPv6DeliveredTotal"
 | 
						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{
 | 
						counterMetrics = map[string]*prometheus.Desc{
 | 
				
			||||||
		// TCP stats
 | 
							// TCP stats
 | 
				
			||||||
		tcpSendTotal: prometheus.NewDesc(
 | 
							tcpSendTotal: prometheus.NewDesc(
 | 
				
			||||||
| 
						 | 
					@ -242,6 +253,30 @@ func getData(queryString string, expectedSize int) ([]byte, error) {
 | 
				
			||||||
	return data, nil
 | 
						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 {
 | 
					type netStatCollector struct {
 | 
				
			||||||
	netStatMetric *prometheus.Desc
 | 
						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
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -323,6 +367,16 @@ func getFreeBSDDataMock(sysctl string) []byte {
 | 
				
			||||||
		size := int(unsafe.Sizeof(C.struct_tcpstat{}))
 | 
							size := int(unsafe.Sizeof(C.struct_tcpstat{}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return unsafe.Slice((*byte)(unsafe.Pointer(&tcpStats)), size)
 | 
							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" {
 | 
						} else if sysctl == "net.inet.udp.stats" {
 | 
				
			||||||
		udpStats := C.struct_udpstat{
 | 
							udpStats := C.struct_udpstat{
 | 
				
			||||||
			udps_opackets: 1234,
 | 
								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) {
 | 
					func TestGetUDPMetrics(t *testing.T) {
 | 
				
			||||||
	testSetup()
 | 
						testSetup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,20 +161,24 @@ func TestGetIPv6Metrics(t *testing.T) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestNetStatCollectorUpdate(t *testing.T) {
 | 
					func TestNetStatCollectorUpdate(t *testing.T) {
 | 
				
			||||||
	ch := make(chan prometheus.Metric, len(counterMetrics))
 | 
					 | 
				
			||||||
	collector := &netStatCollector{
 | 
						collector := &netStatCollector{
 | 
				
			||||||
		netStatMetric: prometheus.NewDesc("netstat_metric", "NetStat Metric", nil, nil),
 | 
							netStatMetric: prometheus.NewDesc("netstat_metric", "NetStat Metric", nil, nil),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						totalMetrics := len(counterMetrics) + len(tcpStates)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch := make(chan prometheus.Metric, totalMetrics)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := collector.Update(ch)
 | 
						err := collector.Update(ch)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal("unexpected error:", err)
 | 
							t.Fatal("unexpected error:", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if got, want := len(ch), len(counterMetrics); got != want {
 | 
						if got, want := len(ch), totalMetrics; got != want {
 | 
				
			||||||
		t.Errorf("metric count mismatch: want %d, got %d", want, got)
 | 
							t.Fatalf("metric count mismatch: want %d, got %d", want, got)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for range len(counterMetrics) {
 | 
						for range totalMetrics {
 | 
				
			||||||
		<-ch
 | 
							<-ch
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue