From bb4d6213443cf62540c6003cbc358da6162dae72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 21 Sep 2024 13:44:23 +0200 Subject: [PATCH] net: expose nic address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- pkg/collector/cpu/const.go | 29 ++ pkg/collector/cpu/cpu.go | 29 -- pkg/collector/net/const.go | 36 ++ pkg/collector/net/net.go | 365 +++++++++++++++++--- pkg/collector/net/net_test.go | 21 -- pkg/headers/iphlpapi/iphlpapi.go | 50 +++ pkg/headers/iphlpapi/type_route_origin.go | 31 ++ pkg/headers/iphlpapi/type_route_protocol.go | 88 +++++ pkg/headers/iphlpapi/types.go | 48 +++ tools/e2e-output.txt | 2 + 10 files changed, 595 insertions(+), 104 deletions(-) create mode 100644 pkg/collector/net/const.go delete mode 100644 pkg/collector/net/net_test.go create mode 100644 pkg/headers/iphlpapi/iphlpapi.go create mode 100644 pkg/headers/iphlpapi/type_route_origin.go create mode 100644 pkg/headers/iphlpapi/type_route_protocol.go create mode 100644 pkg/headers/iphlpapi/types.go diff --git a/pkg/collector/cpu/const.go b/pkg/collector/cpu/const.go index 76916364f..7bc6e6978 100644 --- a/pkg/collector/cpu/const.go +++ b/pkg/collector/cpu/const.go @@ -26,3 +26,32 @@ const ( ProcessorUtilityRate = "% Processor Utility" UserTimeSeconds = "% User Time" ) + +type perflibProcessorInformation struct { + Name string + C1TimeSeconds float64 `perflib:"% C1 Time"` + C2TimeSeconds float64 `perflib:"% C2 Time"` + C3TimeSeconds float64 `perflib:"% C3 Time"` + C1TransitionsTotal float64 `perflib:"C1 Transitions/sec"` + C2TransitionsTotal float64 `perflib:"C2 Transitions/sec"` + C3TransitionsTotal float64 `perflib:"C3 Transitions/sec"` + ClockInterruptsTotal float64 `perflib:"Clock Interrupts/sec"` + DPCsQueuedTotal float64 `perflib:"DPCs Queued/sec"` + DPCTimeSeconds float64 `perflib:"% DPC Time"` + IdleBreakEventsTotal float64 `perflib:"Idle Break Events/sec"` + IdleTimeSeconds float64 `perflib:"% Idle Time"` + InterruptsTotal float64 `perflib:"Interrupts/sec"` + InterruptTimeSeconds float64 `perflib:"% Interrupt Time"` + ParkingStatus float64 `perflib:"Parking Status"` + PerformanceLimitPercent float64 `perflib:"% Performance Limit"` + PriorityTimeSeconds float64 `perflib:"% Priority Time"` + PrivilegedTimeSeconds float64 `perflib:"% Privileged Time"` + PrivilegedUtilitySeconds float64 `perflib:"% Privileged Utility"` + ProcessorFrequencyMHz float64 `perflib:"Processor Frequency"` + ProcessorPerformance float64 `perflib:"% Processor Performance"` + ProcessorMPerf float64 `perflib:"% Processor Performance,secondvalue"` + ProcessorTimeSeconds float64 `perflib:"% Processor Time"` + ProcessorUtilityRate float64 `perflib:"% Processor Utility"` + ProcessorRTC float64 `perflib:"% Processor Utility,secondvalue"` + UserTimeSeconds float64 `perflib:"% User Time"` +} diff --git a/pkg/collector/cpu/cpu.go b/pkg/collector/cpu/cpu.go index cacd6593d..3bbe28d4b 100644 --- a/pkg/collector/cpu/cpu.go +++ b/pkg/collector/cpu/cpu.go @@ -235,35 +235,6 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch return c.collectFull(ctx, logger, ch) } -type perflibProcessorInformation struct { - Name string - C1TimeSeconds float64 `perflib:"% C1 Time"` - C2TimeSeconds float64 `perflib:"% C2 Time"` - C3TimeSeconds float64 `perflib:"% C3 Time"` - C1TransitionsTotal float64 `perflib:"C1 Transitions/sec"` - C2TransitionsTotal float64 `perflib:"C2 Transitions/sec"` - C3TransitionsTotal float64 `perflib:"C3 Transitions/sec"` - ClockInterruptsTotal float64 `perflib:"Clock Interrupts/sec"` - DPCsQueuedTotal float64 `perflib:"DPCs Queued/sec"` - DPCTimeSeconds float64 `perflib:"% DPC Time"` - IdleBreakEventsTotal float64 `perflib:"Idle Break Events/sec"` - IdleTimeSeconds float64 `perflib:"% Idle Time"` - InterruptsTotal float64 `perflib:"Interrupts/sec"` - InterruptTimeSeconds float64 `perflib:"% Interrupt Time"` - ParkingStatus float64 `perflib:"Parking Status"` - PerformanceLimitPercent float64 `perflib:"% Performance Limit"` - PriorityTimeSeconds float64 `perflib:"% Priority Time"` - PrivilegedTimeSeconds float64 `perflib:"% Privileged Time"` - PrivilegedUtilitySeconds float64 `perflib:"% Privileged Utility"` - ProcessorFrequencyMHz float64 `perflib:"Processor Frequency"` - ProcessorPerformance float64 `perflib:"% Processor Performance"` - ProcessorMPerf float64 `perflib:"% Processor Performance,secondvalue"` - ProcessorTimeSeconds float64 `perflib:"% Processor Time"` - ProcessorUtilityRate float64 `perflib:"% Processor Utility"` - ProcessorRTC float64 `perflib:"% Processor Utility,secondvalue"` - UserTimeSeconds float64 `perflib:"% User Time"` -} - func (c *Collector) collectFull(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { data := make([]perflibProcessorInformation, 0) diff --git a/pkg/collector/net/const.go b/pkg/collector/net/const.go new file mode 100644 index 000000000..352866115 --- /dev/null +++ b/pkg/collector/net/const.go @@ -0,0 +1,36 @@ +package net + +const ( + BytesReceivedPerSec = "Bytes Received/sec" + BytesSentPerSec = "Bytes Sent/sec" + BytesTotalPerSec = "Bytes Total/sec" + OutputQueueLength = "Output Queue Length" + PacketsOutboundDiscarded = "Packets Outbound Discarded" + PacketsOutboundErrors = "Packets Outbound Errors" + PacketsPerSec = "Packets/sec" + PacketsReceivedDiscarded = "Packets Received Discarded" + PacketsReceivedErrors = "Packets Received Errors" + PacketsReceivedPerSec = "Packets Received/sec" + PacketsReceivedUnknown = "Packets Received Unknown" + PacketsSentPerSec = "Packets Sent/sec" + CurrentBandwidth = "Current Bandwidth" +) + +// Win32_PerfRawData_Tcpip_NetworkInterface docs: +// - https://technet.microsoft.com/en-us/security/aa394340(v=vs.80) +type perflibNetworkInterface struct { + BytesReceivedPerSec float64 `perflib:"Bytes Received/sec"` + BytesSentPerSec float64 `perflib:"Bytes Sent/sec"` + BytesTotalPerSec float64 `perflib:"Bytes Total/sec"` + Name string + OutputQueueLength float64 `perflib:"Output Queue Length"` + PacketsOutboundDiscarded float64 `perflib:"Packets Outbound Discarded"` + PacketsOutboundErrors float64 `perflib:"Packets Outbound Errors"` + PacketsPerSec float64 `perflib:"Packets/sec"` + PacketsReceivedDiscarded float64 `perflib:"Packets Received Discarded"` + PacketsReceivedErrors float64 `perflib:"Packets Received Errors"` + PacketsReceivedPerSec float64 `perflib:"Packets Received/sec"` + PacketsReceivedUnknown float64 `perflib:"Packets Received Unknown"` + PacketsSentPerSec float64 `perflib:"Packets Sent/sec"` + CurrentBandwidth float64 `perflib:"Current Bandwidth"` +} diff --git a/pkg/collector/net/net.go b/pkg/collector/net/net.go index 4bf449336..824ccf81b 100644 --- a/pkg/collector/net/net.go +++ b/pkg/collector/net/net.go @@ -3,35 +3,49 @@ package net import ( + "errors" "fmt" "log/slog" + "os" "regexp" + "slices" + "strings" + "unsafe" "github.com/alecthomas/kingpin/v2" + "github.com/prometheus-community/windows_exporter/pkg/perfdata" "github.com/prometheus-community/windows_exporter/pkg/perflib" "github.com/prometheus-community/windows_exporter/pkg/types" + "github.com/prometheus-community/windows_exporter/pkg/utils" "github.com/prometheus/client_golang/prometheus" "github.com/yusufpapurcu/wmi" + "golang.org/x/sys/windows" ) const Name = "net" type Config struct { - NicExclude *regexp.Regexp `yaml:"nic_exclude"` - NicInclude *regexp.Regexp `yaml:"nic_include"` + NicExclude *regexp.Regexp `yaml:"nic_exclude"` + NicInclude *regexp.Regexp `yaml:"nic_include"` + CollectorsEnabled []string `yaml:"collectors_enabled"` } var ConfigDefaults = Config{ NicExclude: types.RegExpEmpty, NicInclude: types.RegExpAny, + CollectorsEnabled: []string{ + "metrics", + "addresses", + "routes", + }, } -var nicNameToUnderscore = regexp.MustCompile("[^a-zA-Z0-9]") - // A Collector is a Prometheus Collector for Perflib Network Interface metrics. type Collector struct { config Config + perfDataCollector *perfdata.Collector + bytesReceivedTotal *prometheus.Desc bytesSentTotal *prometheus.Desc bytesTotal *prometheus.Desc @@ -45,6 +59,9 @@ type Collector struct { packetsReceivedUnknown *prometheus.Desc packetsSentTotal *prometheus.Desc currentBandwidth *prometheus.Desc + + nicAddressInfo *prometheus.Desc + routeInfo *prometheus.Desc } func New(config *Config) *Collector { @@ -71,9 +88,12 @@ func NewWithFlags(app *kingpin.Application) *Collector { c := &Collector{ config: ConfigDefaults, } + c.config.CollectorsEnabled = make([]string, 0) var nicExclude, nicInclude string + var collectorsEnabled string + app.Flag( "collector.net.nic-exclude", "Regexp of NIC:s to exclude. NIC name must both match include and not match exclude to be included.", @@ -84,7 +104,14 @@ func NewWithFlags(app *kingpin.Application) *Collector { "Regexp of NIC:s to include. NIC name must both match include and not match exclude to be included.", ).Default(c.config.NicInclude.String()).StringVar(&nicInclude) + app.Flag( + "collectors.net.enabled", + "Comma-separated list of collectors to use. Defaults to all, if not specified.", + ).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled) + app.Action(func(*kingpin.ParseContext) error { + c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",") + var err error c.config.NicExclude, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", nicExclude)) @@ -108,6 +135,10 @@ func (c *Collector) GetName() string { } func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { + if utils.PDHEnabled() { + return []string{}, nil + } + return []string{"Network Interface"}, nil } @@ -116,6 +147,31 @@ func (c *Collector) Close(_ *slog.Logger) error { } func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { + if utils.PDHEnabled() { + counters := []string{ + BytesReceivedPerSec, + BytesSentPerSec, + BytesTotalPerSec, + OutputQueueLength, + PacketsOutboundDiscarded, + PacketsOutboundErrors, + PacketsPerSec, + PacketsReceivedDiscarded, + PacketsReceivedErrors, + PacketsReceivedPerSec, + PacketsReceivedUnknown, + PacketsSentPerSec, + CurrentBandwidth, + } + + var err error + + c.perfDataCollector, err = perfdata.NewCollector("Network Interface", []string{"*"}, counters) + if err != nil { + return fmt.Errorf("failed to create Processor Information collector: %w", err) + } + } + c.bytesReceivedTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "bytes_received_total"), "(Network.BytesReceivedPerSec)", @@ -194,6 +250,18 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { []string{"nic"}, nil, ) + c.nicAddressInfo = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "nic_address_info"), + "A metric with a constant '1' value labeled with the network interface's address information.", + []string{"nic", "friendly_name", "address", "family"}, + nil, + ) + c.routeInfo = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "route_info"), + "A metric with a constant '1' value labeled with the network interface's route information.", + []string{"nic", "src", "dest", "metric"}, + nil, + ) return nil } @@ -202,46 +270,38 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { // to the provided prometheus Metric channel. func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { logger = logger.With(slog.String("collector", Name)) - if err := c.collect(ctx, logger, ch); err != nil { - logger.Error("failed collecting net metrics", - slog.Any("err", err), - ) - return err + if slices.Contains(c.config.CollectorsEnabled, "metrics") { + var err error + + if utils.PDHEnabled() { + err = c.collectPDH(ch) + } else { + err = c.collect(ctx, logger, ch) + } + + if err != nil { + return fmt.Errorf("failed collecting net metrics: %w", err) + } } - return nil -} + if slices.Contains(c.config.CollectorsEnabled, "addresses") { + if err := c.collectNICAddresses(ch); err != nil { + return fmt.Errorf("failed collecting net addresses: %w", err) + } + } -// mangleNetworkName mangles Network Adapter name (non-alphanumeric to _) -// that is used in networkInterface. -func mangleNetworkName(name string) string { - return nicNameToUnderscore.ReplaceAllString(name, "_") -} + if slices.Contains(c.config.CollectorsEnabled, "routes") { + if err := c.collectRoutes(ch); err != nil { + return fmt.Errorf("failed collecting net routes: %w", err) + } + } -// Win32_PerfRawData_Tcpip_NetworkInterface docs: -// - https://technet.microsoft.com/en-us/security/aa394340(v=vs.80) -type networkInterface struct { - BytesReceivedPerSec float64 `perflib:"Bytes Received/sec"` - BytesSentPerSec float64 `perflib:"Bytes Sent/sec"` - BytesTotalPerSec float64 `perflib:"Bytes Total/sec"` - Name string - OutputQueueLength float64 `perflib:"Output Queue Length"` - PacketsOutboundDiscarded float64 `perflib:"Packets Outbound Discarded"` - PacketsOutboundErrors float64 `perflib:"Packets Outbound Errors"` - PacketsPerSec float64 `perflib:"Packets/sec"` - PacketsReceivedDiscarded float64 `perflib:"Packets Received Discarded"` - PacketsReceivedErrors float64 `perflib:"Packets Received Errors"` - PacketsReceivedPerSec float64 `perflib:"Packets Received/sec"` - PacketsReceivedUnknown float64 `perflib:"Packets Received Unknown"` - PacketsSentPerSec float64 `perflib:"Packets Sent/sec"` - CurrentBandwidth float64 `perflib:"Current Bandwidth"` + return nil } func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { - logger = logger.With(slog.String("collector", Name)) - - var dst []networkInterface + var dst []perflibNetworkInterface if err := perflib.UnmarshalObject(ctx.PerfObjects["Network Interface"], &dst, logger); err != nil { return err @@ -253,91 +313,288 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch continue } - name := mangleNetworkName(nic.Name) - if name == "" { - continue - } - // Counters ch <- prometheus.MustNewConstMetric( c.bytesReceivedTotal, prometheus.CounterValue, nic.BytesReceivedPerSec, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.bytesSentTotal, prometheus.CounterValue, nic.BytesSentPerSec, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.bytesTotal, prometheus.CounterValue, nic.BytesTotalPerSec, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.outputQueueLength, prometheus.GaugeValue, nic.OutputQueueLength, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsOutboundDiscarded, prometheus.CounterValue, nic.PacketsOutboundDiscarded, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsOutboundErrors, prometheus.CounterValue, nic.PacketsOutboundErrors, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsTotal, prometheus.CounterValue, nic.PacketsPerSec, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsReceivedDiscarded, prometheus.CounterValue, nic.PacketsReceivedDiscarded, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsReceivedErrors, prometheus.CounterValue, nic.PacketsReceivedErrors, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsReceivedTotal, prometheus.CounterValue, nic.PacketsReceivedPerSec, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsReceivedUnknown, prometheus.CounterValue, nic.PacketsReceivedUnknown, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.packetsSentTotal, prometheus.CounterValue, nic.PacketsSentPerSec, - name, + nic.Name, ) ch <- prometheus.MustNewConstMetric( c.currentBandwidth, prometheus.GaugeValue, nic.CurrentBandwidth/8, - name, + nic.Name, + ) + } + + return nil +} + +func (c *Collector) collectPDH(ch chan<- prometheus.Metric) error { + data, err := c.perfDataCollector.Collect() + if err != nil { + return fmt.Errorf("failed to collect Network Information metrics: %w", err) + } + + for nicName, nicData := range data { + if c.config.NicExclude.MatchString(nicName) || + !c.config.NicInclude.MatchString(nicName) { + continue + } + + // Counters + ch <- prometheus.MustNewConstMetric( + c.bytesReceivedTotal, + prometheus.CounterValue, + nicData[BytesReceivedPerSec].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.bytesSentTotal, + prometheus.CounterValue, + nicData[BytesSentPerSec].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.bytesTotal, + prometheus.CounterValue, + nicData[BytesTotalPerSec].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.outputQueueLength, + prometheus.GaugeValue, + nicData[OutputQueueLength].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsOutboundDiscarded, + prometheus.CounterValue, + nicData[PacketsOutboundDiscarded].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsOutboundErrors, + prometheus.CounterValue, + nicData[PacketsOutboundErrors].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsTotal, + prometheus.CounterValue, + nicData[PacketsPerSec].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsReceivedDiscarded, + prometheus.CounterValue, + nicData[PacketsReceivedDiscarded].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsReceivedErrors, + prometheus.CounterValue, + nicData[PacketsReceivedErrors].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsReceivedTotal, + prometheus.CounterValue, + nicData[PacketsReceivedPerSec].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsReceivedUnknown, + prometheus.CounterValue, + nicData[PacketsReceivedUnknown].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.packetsSentTotal, + prometheus.CounterValue, + nicData[PacketsSentPerSec].FirstValue, + nicName, + ) + ch <- prometheus.MustNewConstMetric( + c.currentBandwidth, + prometheus.GaugeValue, + nicData[CurrentBandwidth].FirstValue/8, + nicName, ) } return nil } + +var addressFamily = map[uint16]string{ + windows.AF_INET: "ipv4", + windows.AF_INET6: "ipv6", +} + +func (c *Collector) collectNICAddresses(ch chan<- prometheus.Metric) error { + nicAdapterAddresses, err := adapterAddresses() + if err != nil { + return err + } + + convertNicName := strings.NewReplacer("(", "[", ")", "]") + + for _, nicAdapterAddress := range nicAdapterAddresses { + friendlyName := windows.UTF16PtrToString(nicAdapterAddress.FriendlyName) + nicName := windows.UTF16PtrToString(nicAdapterAddress.Description) + + if c.config.NicExclude.MatchString(nicName) || + !c.config.NicInclude.MatchString(nicName) { + continue + } + + for address := nicAdapterAddress.FirstUnicastAddress; address != nil; address = address.Next { + ipAddr := address.Address.IP() + + if ipAddr == nil || !ipAddr.IsGlobalUnicast() { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.nicAddressInfo, + prometheus.GaugeValue, + 1, + convertNicName.Replace(nicName), + friendlyName, + ipAddr.String(), + addressFamily[address.Address.Sockaddr.Addr.Family], + ) + } + + for address := nicAdapterAddress.FirstAnycastAddress; address != nil; address = address.Next { + ipAddr := address.Address.IP() + + if ipAddr == nil || !ipAddr.IsGlobalUnicast() { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.nicAddressInfo, + prometheus.GaugeValue, + 1, + convertNicName.Replace(nicName), + friendlyName, + ipAddr.String(), + addressFamily[address.Address.Sockaddr.Addr.Family], + ) + } + } + + return nil +} + +func (c *Collector) collectRoutes(_ chan<- prometheus.Metric) error { + return nil +} + +// adapterAddresses returns a list of IP adapter and address +// structures. The structure contains an IP adapter and flattened +// multiple IP addresses including unicast, anycast and multicast +// addresses. +func adapterAddresses() ([]*windows.IpAdapterAddresses, error) { + var b []byte + + l := uint32(15000) // recommended initial size + + for { + b = make([]byte, l) + + const flags = windows.GAA_FLAG_SKIP_MULTICAST | windows.GAA_FLAG_SKIP_DNS_SERVER + + err := windows.GetAdaptersAddresses(windows.AF_UNSPEC, flags, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l) + if err == nil { + if l == 0 { + return nil, nil + } + + break + } + + if !errors.Is(err, windows.ERROR_BUFFER_OVERFLOW) { + return nil, os.NewSyscallError("getadaptersaddresses", err) + } + + if l <= uint32(len(b)) { + return nil, os.NewSyscallError("getadaptersaddresses", err) + } + } + + var addresses []*windows.IpAdapterAddresses + for address := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); address != nil; address = address.Next { + addresses = append(addresses, address) + } + + return addresses, nil +} diff --git a/pkg/collector/net/net_test.go b/pkg/collector/net/net_test.go deleted file mode 100644 index 3dc170907..000000000 --- a/pkg/collector/net/net_test.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build windows - -package net - -import ( - "testing" -) - -func TestNetworkToInstanceName(t *testing.T) { - t.Parallel() - - data := map[string]string{ - "Intel[R] Dual Band Wireless-AC 8260": "Intel_R__Dual_Band_Wireless_AC_8260", - } - for in, out := range data { - got := mangleNetworkName(in) - if got != out { - t.Error("expected", out, "got", got) - } - } -} diff --git a/pkg/headers/iphlpapi/iphlpapi.go b/pkg/headers/iphlpapi/iphlpapi.go new file mode 100644 index 000000000..9cd79550f --- /dev/null +++ b/pkg/headers/iphlpapi/iphlpapi.go @@ -0,0 +1,50 @@ +package iphlpapi + +import ( + "fmt" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + + procGetIPForwardTable2 = modiphlpapi.NewProc("GetIpForwardTable2") + procFreeMibTable = modiphlpapi.NewProc("FreeMibTable") +) + +// GetIpForwardTable2 function +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardtable2). +func GetIpForwardTable2(family uint32) ([]*MIB_IPFORWARD_ROW2, error) { + var pTable *MIB_IPFORWARD_TABLE2 + + r1, _, err := procGetIPForwardTable2.Call( + uintptr(family), + uintptr(unsafe.Pointer(&pTable)), + ) + + if r1 != 1 { + return nil, fmt.Errorf("GetIpForwardTable2: %w", err) + } + + if pTable != nil { + defer func() { + _, _, _ = procFreeMibTable.Call(uintptr(unsafe.Pointer(pTable))) + }() + } + + rows := make([]*MIB_IPFORWARD_ROW2, pTable.NumEntries) + + pFirstRow := uintptr(unsafe.Pointer(&pTable.Table[0])) + rowSize := unsafe.Sizeof(pTable.Table[0]) + + for i := range pTable.NumEntries { + // Dereferencing and rereferencing in order to force copying. + + row := *(*MIB_IPFORWARD_ROW2)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) + rows[i] = &row + } + + return rows, nil +} diff --git a/pkg/headers/iphlpapi/type_route_origin.go b/pkg/headers/iphlpapi/type_route_origin.go new file mode 100644 index 000000000..ff38782e9 --- /dev/null +++ b/pkg/headers/iphlpapi/type_route_origin.go @@ -0,0 +1,31 @@ +package iphlpapi + +import "fmt" + +// NL_ROUTE_ORIGIN defined in nldef.h. +type NL_ROUTE_ORIGIN uint32 + +const ( + NL_ROUTE_ORIGIN_Manual NL_ROUTE_ORIGIN = iota + NL_ROUTE_ORIGIN_WellKnown + NL_ROUTE_ORIGIN_DHCP + NL_ROUTE_ORIGIN_RouterAdvertisement + NL_ROUTE_ORIGIN_6to4 +) + +func (o NL_ROUTE_ORIGIN) String() string { + switch o { + case NL_ROUTE_ORIGIN_Manual: + return "Manual" + case NL_ROUTE_ORIGIN_WellKnown: + return "WellKnown" + case NL_ROUTE_ORIGIN_DHCP: + return "DHCP" + case NL_ROUTE_ORIGIN_RouterAdvertisement: + return "RouterAdvertisement" + case NL_ROUTE_ORIGIN_6to4: + return "6to4" + default: + return fmt.Sprintf("NlRouteOrigin_UNKNOWN(%d)", o) + } +} diff --git a/pkg/headers/iphlpapi/type_route_protocol.go b/pkg/headers/iphlpapi/type_route_protocol.go new file mode 100644 index 000000000..e22ea3010 --- /dev/null +++ b/pkg/headers/iphlpapi/type_route_protocol.go @@ -0,0 +1,88 @@ +package iphlpapi + +import "fmt" + +// NL_ROUTE_PROTOCOL defined in nldef.h. +// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-nl_route_protocol +type NL_ROUTE_PROTOCOL uint32 + +const ( + _ NL_ROUTE_PROTOCOL = iota + RouteProtocolOther + RouteProtocolLocal + RouteProtocolNetMgmt + RouteProtocolIcmp + RouteProtocolEgp + RouteProtocolGgp + RouteProtocolHello + RouteProtocolRip + RouteProtocolIsIs + RouteProtocolEsIs + RouteProtocolCisco + RouteProtocolBbn + RouteProtocolOspf + RouteProtocolBgp + RouteProtocolIdpr + RouteProtocolEigrp + RouteProtocolDvmrp + RouteProtocolRpl + RouteProtocolDhcp + + // + // Windows-specific definitions. + // + NT_AUTOSTATIC NL_ROUTE_PROTOCOL = 10002 + NT_STATIC NL_ROUTE_PROTOCOL = 10006 + NT_STATIC_NON_DOD NL_ROUTE_PROTOCOL = 10007 +) + +func (protocol NL_ROUTE_PROTOCOL) String() string { + switch protocol { + case RouteProtocolOther: + return "Other" + case RouteProtocolLocal: + return "Local" + case RouteProtocolNetMgmt: + return "NetMgmt" + case RouteProtocolIcmp: + return "Icmp" + case RouteProtocolEgp: + return "Egp" + case RouteProtocolGgp: + return "Ggp" + case RouteProtocolHello: + return "Hello" + case RouteProtocolRip: + return "Rip" + case RouteProtocolIsIs: + return "IsIs" + case RouteProtocolEsIs: + return "EsIs" + case RouteProtocolCisco: + return "Cisco" + case RouteProtocolBbn: + return "Bbn" + case RouteProtocolOspf: + return "Ospf" + case RouteProtocolBgp: + return "Bgp" + case RouteProtocolIdpr: + return "Idpr" + case RouteProtocolEigrp: + return "Eigrp" + case RouteProtocolDvmrp: + return "Dvmrp" + case RouteProtocolRpl: + return "Rpl" + case RouteProtocolDhcp: + return "Dhcp" + case NT_AUTOSTATIC: + return "NT_AUTOSTATIC" + case NT_STATIC: + return "NT_STATIC" + case NT_STATIC_NON_DOD: + return "NT_STATIC_NON_DOD" + default: + return fmt.Sprintf("NlRouteProtocol_UNKNOWN(%d)", protocol) + } +} diff --git a/pkg/headers/iphlpapi/types.go b/pkg/headers/iphlpapi/types.go new file mode 100644 index 000000000..b295c892a --- /dev/null +++ b/pkg/headers/iphlpapi/types.go @@ -0,0 +1,48 @@ +package iphlpapi + +// AddressFamily defined in ws2def.h. +type AddressFamily uint16 + +// MIB_IPFORWARD_TABLE2 https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipforward_table2 +type MIB_IPFORWARD_TABLE2 struct { + NumEntries uint32 + Table [1]MIB_IPFORWARD_ROW2 +} + +type MIB_IPFORWARD_ROW2 struct { + InterfaceLuid uint64 + InterfaceIndex uint32 + DestinationPrefix IP_ADDRESS_PREFIX + NextHop SOCKADDR_INET + SitePrefixLength uint8 + ValidLifetime uint32 + PreferredLifetime uint32 + Metric uint32 + Protocol NL_ROUTE_PROTOCOL + Loopback uint8 + AutoconfigureAddress uint8 + Publish uint8 + Immortal uint8 + Age uint32 + Origin NL_ROUTE_ORIGIN +} + +// IP_ADDRESS_PREFIX defined in netioapi.h +// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_ip_address_prefix +type IP_ADDRESS_PREFIX struct { + Prefix SOCKADDR_INET + PrefixLength uint8 +} + +// SOCKADDR_INET https://learn.microsoft.com/de-de/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_in6_w2ksp1 +type SOCKADDR_INET struct { + sin6_family AddressFamily + // Transport level port number. + sin6_port uint16 // Windows type: USHORT + // IPv6 flow information. + sin6_flowinfo uint32 // Windows type: ULONG + // IPv6 address. + sin6_addr [16]uint8 + // Set of interfaces for a scope. + sin6_scope_id uint32 // Windows type: ULONG +} diff --git a/tools/e2e-output.txt b/tools/e2e-output.txt index 72a0dc057..fb1da2a44 100644 --- a/tools/e2e-output.txt +++ b/tools/e2e-output.txt @@ -29,6 +29,8 @@ test_alpha_total 42 # TYPE windows_cpu_interrupts_total counter # HELP windows_cpu_logical_processor Total number of logical processors # TYPE windows_cpu_logical_processor gauge +# HELP windows_net_nic_address_info A metric with a constant '1' value labeled with the network interface's address information. +# TYPE windows_net_nic_address_info gauge # HELP windows_cpu_parking_status Parking Status represents whether a processor is parked or not # TYPE windows_cpu_parking_status gauge # HELP windows_cpu_processor_mperf_total Processor MPerf is the number of TSC ticks incremented while executing instructions