diff --git a/collector/netstat_freebsd.go b/collector/netstat_freebsd.go new file mode 100644 index 0000000000..9e59ae3c6d --- /dev/null +++ b/collector/netstat_freebsd.go @@ -0,0 +1,108 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build freebsd +// +build freebsd + +package collector + +import ( + "errors" + "fmt" + "log/slog" + "unsafe" + + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sys/unix" +) + +/* +#include +#include +#include +#include +#include +#include +*/ +import "C" + +var metricDescs = []*prometheus.Desc{ + prometheus.NewDesc( + "tcp_send_packet_total", + "tcp_send_packet_total", + nil, nil, + ), + prometheus.NewDesc( + "tcp_recv_packet_total", + "tcp_recv_packet_total", + nil, nil, + ), +} + +type netStatCollector struct { + netStatMetric *prometheus.Desc +} + +func init() { + registerCollector("netstat", defaultEnabled, NewNetStatCollector) +} + +func NewNetStatCollector(logger *slog.Logger) (Collector, error) { + return &netStatCollector{}, nil +} + +func (c *netStatCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.netStatMetric +} + +func (c *netStatCollector) Collect(ch chan<- prometheus.Metric) { + _ = c.Update(ch) +} + +func getData(queryString string) ([]byte, error) { + data, err := unix.SysctlRaw(queryString) + if err != nil { + fmt.Println("Error:", err) + return nil, err + } + + if len(data) < int(unsafe.Sizeof(C.struct_tcpstat{})) { + return nil, errors.New("Data Size mismatch") + } + return data, nil +} + +func (c *netStatCollector) Update(ch chan<- prometheus.Metric) error { + + var result []float64 + + tcpData, err := getData("net.inet.tcp.stats") + if err != nil { + return err + } + + tcpStats := *(*C.struct_tcpstat)(unsafe.Pointer(&tcpData[0])) + + result = append(result, float64(tcpStats.tcps_sndtotal)) + result = append(result, float64(tcpStats.tcps_rcvtotal)) + + for index, value := range metricDescs { + ch <- prometheus.MustNewConstMetric( + value, + prometheus.UntypedValue, + result[index], + ) + } + + return nil +} diff --git a/collector/netstat_freebsd_test.go b/collector/netstat_freebsd_test.go new file mode 100644 index 0000000000..3b3f852335 --- /dev/null +++ b/collector/netstat_freebsd_test.go @@ -0,0 +1,77 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build freebsd +// +build freebsd + +package collector + +import ( + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sys/unix" + "testing" + "unsafe" +) + +func TestNetStatCollectorDescribe(t *testing.T) { + ch := make(chan *prometheus.Desc, 1) + collector := &netStatCollector{ + netStatMetric: prometheus.NewDesc("dummy_metric", "dummy", nil, nil), + } + collector.Describe(ch) + desc := <-ch + + if want, got := "dummy_metric", desc.String(); want != got { + t.Errorf("want %s, got %s", want, got) + } +} + +func TestGetData(t *testing.T) { + data, err := getData("net.inet.tcp.stats") + if err != nil { + t.Fatal("unexpected error:", err) + } + + if got, want := len(data), int(unsafe.Sizeof(unix.TCPStats{})); got < want { + t.Errorf("data length too small: want >= %d, got %d", want, got) + } +} + +func TestNetStatCollectorUpdate(t *testing.T) { + ch := make(chan prometheus.Metric, len(metrics)) + collector := &netStatCollector{ + netStatMetric: prometheus.NewDesc("netstat_metric", "NetStat Metric", nil, nil), + } + err := collector.Update(ch) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if got, want := len(ch), len(metrics); got != want { + t.Errorf("metric count mismatch: want %d, got %d", want, got) + } + + for range metrics { + <-ch + } +} + +func TestNewNetStatCollector(t *testing.T) { + collector, err := NewNetStatCollector(nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + if collector == nil { + t.Fatal("collector is nil, want non-nil") + } +}