From 0a3b0046bbff399ccf58b6177faa07795748ae82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 18 Jan 2025 02:17:24 +0100 Subject: [PATCH 1/6] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/collector/dhcp/dhcp.go | 572 +++++++++++++++------ internal/headers/dhcpsapi/dhcpsapi.go | 212 ++++++++ internal/headers/dhcpsapi/dhcpsapi_test.go | 18 + internal/headers/dhcpsapi/types.go | 135 +++++ internal/headers/win32api/types.go | 13 + 5 files changed, 791 insertions(+), 159 deletions(-) create mode 100644 internal/headers/dhcpsapi/dhcpsapi.go create mode 100644 internal/headers/dhcpsapi/dhcpsapi_test.go create mode 100644 internal/headers/dhcpsapi/types.go create mode 100644 internal/headers/win32api/types.go diff --git a/internal/collector/dhcp/dhcp.go b/internal/collector/dhcp/dhcp.go index 71540bd60..0e5802b35 100644 --- a/internal/collector/dhcp/dhcp.go +++ b/internal/collector/dhcp/dhcp.go @@ -16,22 +16,39 @@ package dhcp import ( + "errors" "fmt" "log/slog" + "slices" + "strconv" + "strings" "github.com/alecthomas/kingpin/v2" + "github.com/prometheus-community/windows_exporter/internal/headers/dhcpsapi" "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/pdh" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" ) -const Name = "dhcp" +const ( + Name = "dhcp" -type Config struct{} + subCollectorServerMetrics = "server_metrics" + subCollectorScopeMetrics = "scope_metrics" +) + +type Config struct { + CollectorsEnabled []string `yaml:"collectors_enabled"` +} //nolint:gochecknoglobals -var ConfigDefaults = Config{} +var ConfigDefaults = Config{ + CollectorsEnabled: []string{ + subCollectorServerMetrics, + subCollectorScopeMetrics, + }, +} // A Collector is a Prometheus Collector perflib DHCP metrics. type Collector struct { @@ -65,6 +82,17 @@ type Collector struct { packetsReceivedTotal *prometheus.Desc releasesTotal *prometheus.Desc requestsTotal *prometheus.Desc + + scopeInfo *prometheus.Desc + scopeState *prometheus.Desc + scopeAddressesFreeTotal *prometheus.Desc + scopeAddressesFreeOnPartnerServerTotal *prometheus.Desc + scopeAddressesFreeOnThisServerTotal *prometheus.Desc + scopeAddressesInUseTotal *prometheus.Desc + scopeAddressesInUseOnPartnerServerTotal *prometheus.Desc + scopeAddressesInUseOnThisServerTotal *prometheus.Desc + scopePendingOffersTotal *prometheus.Desc + scopeReservedAddressTotal *prometheus.Desc } func New(config *Config) *Collector { @@ -72,6 +100,10 @@ func New(config *Config) *Collector { config = &ConfigDefaults } + if config.CollectorsEnabled == nil { + config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled + } + c := &Collector{ config: *config, } @@ -79,8 +111,26 @@ func New(config *Config) *Collector { return c } -func NewWithFlags(_ *kingpin.Application) *Collector { - return &Collector{} +func NewWithFlags(app *kingpin.Application) *Collector { + c := &Collector{ + config: ConfigDefaults, + } + c.config.CollectorsEnabled = make([]string, 0) + + var collectorsEnabled string + + app.Flag( + "collector.dhcp.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, ",") + + return nil + }) + + return c } func (c *Collector) GetName() string { @@ -88,7 +138,9 @@ func (c *Collector) GetName() string { } func (c *Collector) Close() error { - c.perfDataCollector.Close() + if slices.Contains(c.config.CollectorsEnabled, subCollectorServerMetrics) { + c.perfDataCollector.Close() + } return nil } @@ -96,166 +148,258 @@ func (c *Collector) Close() error { func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { var err error - c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](pdh.CounterTypeRaw, "DHCP Server", nil) - if err != nil { - return fmt.Errorf("failed to create DHCP Server collector: %w", err) + if slices.Contains(c.config.CollectorsEnabled, subCollectorServerMetrics) { + c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](pdh.CounterTypeRaw, "DHCP Server", nil) + if err != nil { + return fmt.Errorf("failed to create DHCP Server collector: %w", err) + } + + c.packetsReceivedTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "packets_received_total"), + "Total number of packets received by the DHCP server (PacketsReceivedTotal)", + nil, + nil, + ) + c.duplicatesDroppedTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "duplicates_dropped_total"), + "Total number of duplicate packets received by the DHCP server (DuplicatesDroppedTotal)", + nil, + nil, + ) + c.packetsExpiredTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "packets_expired_total"), + "Total number of packets expired in the DHCP server message queue (PacketsExpiredTotal)", + nil, + nil, + ) + c.activeQueueLength = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "active_queue_length"), + "Number of packets in the processing queue of the DHCP server (ActiveQueueLength)", + nil, + nil, + ) + c.conflictCheckQueueLength = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "conflict_check_queue_length"), + "Number of packets in the DHCP server queue waiting on conflict detection (ping). (ConflictCheckQueueLength)", + nil, + nil, + ) + c.discoversTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "discovers_total"), + "Total DHCP Discovers received by the DHCP server (DiscoversTotal)", + nil, + nil, + ) + c.offersTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "offers_total"), + "Total DHCP Offers sent by the DHCP server (OffersTotal)", + nil, + nil, + ) + c.requestsTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "requests_total"), + "Total DHCP Requests received by the DHCP server (RequestsTotal)", + nil, + nil, + ) + c.informsTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "informs_total"), + "Total DHCP Informs received by the DHCP server (InformsTotal)", + nil, + nil, + ) + c.acksTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "acks_total"), + "Total DHCP Acks sent by the DHCP server (AcksTotal)", + nil, + nil, + ) + c.nACKsTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "nacks_total"), + "Total DHCP Nacks sent by the DHCP server (NacksTotal)", + nil, + nil, + ) + c.declinesTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "declines_total"), + "Total DHCP Declines received by the DHCP server (DeclinesTotal)", + nil, + nil, + ) + c.releasesTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "releases_total"), + "Total DHCP Releases received by the DHCP server (ReleasesTotal)", + nil, + nil, + ) + c.offerQueueLength = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "offer_queue_length"), + "Number of packets in the offer queue of the DHCP server (OfferQueueLength)", + nil, + nil, + ) + c.deniedDueToMatch = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_match_total"), + "Total number of DHCP requests denied, based on matches from the Deny list (DeniedDueToMatch)", + nil, + nil, + ) + c.deniedDueToNonMatch = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_nonmatch_total"), + "Total number of DHCP requests denied, based on non-matches from the Allow list (DeniedDueToNonMatch)", + nil, + nil, + ) + c.failoverBndUpdSentTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_sent_total"), + "Number of DHCP fail over Binding Update messages sent (FailoverBndupdSentTotal)", + nil, + nil, + ) + c.failoverBndUpdReceivedTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_received_total"), + "Number of DHCP fail over Binding Update messages received (FailoverBndupdReceivedTotal)", + nil, + nil, + ) + c.failoverBndAckSentTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_sent_total"), + "Number of DHCP fail over Binding Ack messages sent (FailoverBndackSentTotal)", + nil, + nil, + ) + c.failoverBndAckReceivedTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_received_total"), + "Number of DHCP fail over Binding Ack messages received (FailoverBndackReceivedTotal)", + nil, + nil, + ) + c.failoverBndUpdPendingOutboundQueue = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_pending_in_outbound_queue"), + "Number of pending outbound DHCP fail over Binding Update messages (FailoverBndupdPendingOutboundQueue)", + nil, + nil, + ) + c.failoverTransitionsCommunicationInterruptedState = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_communicationinterrupted_state_total"), + "Total number of transitions into COMMUNICATION INTERRUPTED state (FailoverTransitionsCommunicationinterruptedState)", + nil, + nil, + ) + c.failoverTransitionsPartnerDownState = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_partnerdown_state_total"), + "Total number of transitions into PARTNER DOWN state (FailoverTransitionsPartnerdownState)", + nil, + nil, + ) + c.failoverTransitionsRecoverState = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_recover_total"), + "Total number of transitions into RECOVER state (FailoverTransitionsRecoverState)", + nil, + nil, + ) + c.failoverBndUpdDropped = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_dropped_total"), + "Total number of DHCP fail over Binding Updates dropped (FailoverBndupdDropped)", + nil, + nil, + ) } - c.packetsReceivedTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "packets_received_total"), - "Total number of packets received by the DHCP server (PacketsReceivedTotal)", - nil, - nil, - ) - c.duplicatesDroppedTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "duplicates_dropped_total"), - "Total number of duplicate packets received by the DHCP server (DuplicatesDroppedTotal)", - nil, - nil, - ) - c.packetsExpiredTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "packets_expired_total"), - "Total number of packets expired in the DHCP server message queue (PacketsExpiredTotal)", - nil, - nil, - ) - c.activeQueueLength = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "active_queue_length"), - "Number of packets in the processing queue of the DHCP server (ActiveQueueLength)", - nil, - nil, - ) - c.conflictCheckQueueLength = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "conflict_check_queue_length"), - "Number of packets in the DHCP server queue waiting on conflict detection (ping). (ConflictCheckQueueLength)", - nil, - nil, - ) - c.discoversTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "discovers_total"), - "Total DHCP Discovers received by the DHCP server (DiscoversTotal)", - nil, - nil, - ) - c.offersTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "offers_total"), - "Total DHCP Offers sent by the DHCP server (OffersTotal)", - nil, - nil, - ) - c.requestsTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "requests_total"), - "Total DHCP Requests received by the DHCP server (RequestsTotal)", - nil, - nil, - ) - c.informsTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "informs_total"), - "Total DHCP Informs received by the DHCP server (InformsTotal)", - nil, - nil, - ) - c.acksTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "acks_total"), - "Total DHCP Acks sent by the DHCP server (AcksTotal)", - nil, - nil, - ) - c.nACKsTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "nacks_total"), - "Total DHCP Nacks sent by the DHCP server (NacksTotal)", - nil, - nil, - ) - c.declinesTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "declines_total"), - "Total DHCP Declines received by the DHCP server (DeclinesTotal)", - nil, - nil, - ) - c.releasesTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "releases_total"), - "Total DHCP Releases received by the DHCP server (ReleasesTotal)", - nil, - nil, - ) - c.offerQueueLength = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "offer_queue_length"), - "Number of packets in the offer queue of the DHCP server (OfferQueueLength)", - nil, - nil, - ) - c.deniedDueToMatch = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_match_total"), - "Total number of DHCP requests denied, based on matches from the Deny list (DeniedDueToMatch)", - nil, - nil, - ) - c.deniedDueToNonMatch = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_nonmatch_total"), - "Total number of DHCP requests denied, based on non-matches from the Allow list (DeniedDueToNonMatch)", - nil, - nil, - ) - c.failoverBndUpdSentTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_sent_total"), - "Number of DHCP fail over Binding Update messages sent (FailoverBndupdSentTotal)", - nil, - nil, - ) - c.failoverBndUpdReceivedTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_received_total"), - "Number of DHCP fail over Binding Update messages received (FailoverBndupdReceivedTotal)", - nil, - nil, - ) - c.failoverBndAckSentTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_sent_total"), - "Number of DHCP fail over Binding Ack messages sent (FailoverBndackSentTotal)", - nil, - nil, - ) - c.failoverBndAckReceivedTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_received_total"), - "Number of DHCP fail over Binding Ack messages received (FailoverBndackReceivedTotal)", - nil, - nil, - ) - c.failoverBndUpdPendingOutboundQueue = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_pending_in_outbound_queue"), - "Number of pending outbound DHCP fail over Binding Update messages (FailoverBndupdPendingOutboundQueue)", - nil, - nil, - ) - c.failoverTransitionsCommunicationInterruptedState = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_communicationinterrupted_state_total"), - "Total number of transitions into COMMUNICATION INTERRUPTED state (FailoverTransitionsCommunicationinterruptedState)", - nil, - nil, - ) - c.failoverTransitionsPartnerDownState = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_partnerdown_state_total"), - "Total number of transitions into PARTNER DOWN state (FailoverTransitionsPartnerdownState)", - nil, - nil, - ) - c.failoverTransitionsRecoverState = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_recover_total"), - "Total number of transitions into RECOVER state (FailoverTransitionsRecoverState)", - nil, - nil, - ) - c.failoverBndUpdDropped = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_dropped_total"), - "Total number of DHCP fail over Binding Updates dropped (FailoverBndupdDropped)", - nil, - nil, - ) + if slices.Contains(c.config.CollectorsEnabled, subCollectorScopeMetrics) { + c.scopeInfo = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_info"), + "DHCP Scope information", + []string{"name", "superscope_name", "superscope_id", "scope"}, + nil, + ) + + c.scopeState = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_state"), + "DHCP Scope state", + []string{"scope", "state"}, + nil, + ) + + c.scopeAddressesFreeTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_total"), + "DHCP Scope free addresses", + []string{"scope"}, + nil, + ) + + c.scopeAddressesFreeOnPartnerServerTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_partner_server_total"), + "DHCP Scope free addresses on partner server", + []string{"scope"}, + nil, + ) + + c.scopeAddressesFreeOnThisServerTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_this_server_total"), + "DHCP Scope free addresses on this server", + []string{"scope"}, + nil, + ) + + c.scopeAddressesInUseTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_total"), + "DHCP Scope addresses in use", + []string{"scope"}, + nil, + ) + + c.scopeAddressesInUseOnPartnerServerTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_partner_server_total"), + "DHCP Scope addresses in use on partner server", + []string{"scope"}, + nil, + ) + + c.scopeAddressesInUseOnThisServerTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_this_server_total"), + "DHCP Scope addresses in use on this server", + []string{"scope"}, + nil, + ) + + c.scopePendingOffersTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_pending_offers_total"), + "DHCP Scope pending offers", + []string{"scope"}, + nil, + ) + + c.scopeReservedAddressTotal = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "scope_reserved_address_total"), + "DHCP Scope reserved addresses", + []string{"scope"}, + nil, + ) + } return nil } func (c *Collector) Collect(ch chan<- prometheus.Metric) error { + var errs []error + + if slices.Contains(c.config.CollectorsEnabled, subCollectorServerMetrics) { + if err := c.collectServerMetrics(ch); err != nil { + errs = append(errs, err) + } + } + + if slices.Contains(c.config.CollectorsEnabled, subCollectorScopeMetrics) { + if err := c.collectScopeMetrics(ch); err != nil { + errs = append(errs, err) + } + } + + return errors.Join(errs...) +} + +func (c *Collector) collectServerMetrics(ch chan<- prometheus.Metric) error { err := c.perfDataCollector.Collect(&c.perfDataObject) if err != nil { return fmt.Errorf("failed to collect DHCP Server metrics: %w", err) @@ -413,3 +557,113 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error { return nil } + +func (c *Collector) collectScopeMetrics(ch chan<- prometheus.Metric) error { + dhcpScopes, err := dhcpsapi.GetDHCPV4ScopeStatistics() + if err != nil { + return fmt.Errorf("failed to get DHCP scopes: %w", err) + } + + for _, scope := range dhcpScopes { + scopeID := scope.ScopeIPAddress.String() + + ch <- prometheus.MustNewConstMetric( + c.scopeInfo, + prometheus.GaugeValue, + 1, + scope.Name, + scope.SuperScopeName, + strconv.Itoa(int(scope.SuperScopeNumber)), + scopeID, + ) + + for state, name := range dhcpsapi.DHCP_SUBNET_STATE_NAMES { + metric := 0.0 + if state == scope.State { + metric = 1.0 + } + + ch <- prometheus.MustNewConstMetric( + c.scopeState, + prometheus.GaugeValue, + metric, + scopeID, + name, + ) + } + + if scope.AddressesFree != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeAddressesFreeTotal, + prometheus.GaugeValue, + scope.AddressesFree, + scopeID, + ) + } + + if scope.AddressesFreeOnPartnerServer != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeAddressesFreeOnPartnerServerTotal, + prometheus.GaugeValue, + scope.AddressesFreeOnPartnerServer, + scopeID, + ) + } + + if scope.AddressesFreeOnThisServer != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeAddressesFreeOnThisServerTotal, + prometheus.GaugeValue, + scope.AddressesFreeOnThisServer, + scopeID, + ) + } + + if scope.AddressesInUse != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeAddressesInUseTotal, + prometheus.GaugeValue, + scope.AddressesInUse, + scopeID, + ) + } + + if scope.AddressesInUseOnPartnerServer != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeAddressesInUseOnPartnerServerTotal, + prometheus.GaugeValue, + scope.AddressesInUseOnPartnerServer, + scopeID, + ) + } + + if scope.AddressesInUseOnThisServer != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeAddressesInUseOnThisServerTotal, + prometheus.GaugeValue, + scope.AddressesInUseOnThisServer, + scopeID, + ) + } + + if scope.PendingOffers != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopePendingOffersTotal, + prometheus.GaugeValue, + scope.PendingOffers, + scopeID, + ) + } + + if scope.ReservedAddress != -1 { + ch <- prometheus.MustNewConstMetric( + c.scopeReservedAddressTotal, + prometheus.GaugeValue, + scope.ReservedAddress, + scopeID, + ) + } + } + + return nil +} diff --git a/internal/headers/dhcpsapi/dhcpsapi.go b/internal/headers/dhcpsapi/dhcpsapi.go new file mode 100644 index 000000000..8270b165a --- /dev/null +++ b/internal/headers/dhcpsapi/dhcpsapi.go @@ -0,0 +1,212 @@ +package dhcpsapi + +import ( + "errors" + "fmt" + "net" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +//nolint:gochecknoglobals +var ( + modDhcpServer = windows.NewLazySystemDLL("dhcpsapi.dll") + procDhcpGetSubnetInfo = modDhcpServer.NewProc("DhcpGetSubnetInfo") + procDhcpGetSuperScopeInfoV4 = modDhcpServer.NewProc("DhcpGetSuperScopeInfoV4") + procDhcpRpcFreeMemory = modDhcpServer.NewProc("DhcpRpcFreeMemory") + procDhcpV4EnumSubnetReservations = modDhcpServer.NewProc("DhcpV4EnumSubnetReservations") + procDhcpV4FailoverGetScopeStatistics = modDhcpServer.NewProc("DhcpV4FailoverGetScopeStatistics") + procDhcpGetMibInfoV5 = modDhcpServer.NewProc("DhcpGetMibInfoV5") +) + +func GetDHCPV4ScopeStatistics() ([]DHCPV4Scope, error) { + var mibInfo *DHCP_MIB_INFO_V5 + + if err := dhcpGetMibInfoV5(&mibInfo); err != nil { + return nil, err + } + + defer dhcpRpcFreeMemory(unsafe.Pointer(&mibInfo)) + + subnetScopeInfos := make(map[DHCP_IP_ADDRESS]DHCP_SUBNET_MIB_INFO_V5, mibInfo.Scopes) + subnetMIBScopeInfos := unsafe.Slice(mibInfo.ScopeInfo, mibInfo.Scopes) + + for _, subnetMIBScopeInfo := range subnetMIBScopeInfos { + subnetScopeInfos[subnetMIBScopeInfo.Subnet] = subnetMIBScopeInfo + } + + var superScopeTable *DHCP_SUPER_SCOPE_TABLE + + if err := dhcpGetSuperScopeInfoV4(&superScopeTable); err != nil { + return nil, err + } else if superScopeTable == nil { + return nil, errors.New("dhcpGetSuperScopeInfoV4 returned nil") + } + + defer dhcpRpcFreeMemory(unsafe.Pointer(&superScopeTable)) + + scopes := make([]DHCPV4Scope, 0, superScopeTable.Count) + subnets := unsafe.Slice(superScopeTable.Entries, superScopeTable.Count) + + var errs []error + + for _, subnet := range subnets { + if err := (func() error { + var subnetInfo *DHCP_SUBNET_INFO + err := dhcpGetSubnetInfo(subnet.SubnetAddress, &subnetInfo) + if err != nil { + return fmt.Errorf("failed to get subnet info: %w", err) + } + + defer dhcpRpcFreeMemory(unsafe.Pointer(&subnetInfo)) + + scope := DHCPV4Scope{ + Name: subnetInfo.SubnetName.String(), + SuperScopeName: subnet.SuperScopeName.String(), + ScopeIPAddress: net.IPNet{IP: subnetInfo.SubnetAddress.IPv4(), Mask: subnetInfo.SubnetMask.IPv4Mask()}, + SuperScopeNumber: subnet.SuperScopeNumber, + State: subnetInfo.SubnetState, + + AddressesFree: -1, + AddressesFreeOnPartnerServer: -1, + AddressesFreeOnThisServer: -1, + AddressesInUse: -1, + AddressesInUseOnPartnerServer: -1, + AddressesInUseOnThisServer: -1, + PendingOffers: -1, + ReservedAddress: -1, + } + + if subnetScopeInfo, ok := subnetScopeInfos[subnetInfo.SubnetAddress]; ok { + scope.AddressesInUse = float64(subnetScopeInfo.NumAddressesInUse) + scope.AddressesFree = float64(subnetScopeInfo.NumAddressesFree) + scope.PendingOffers = float64(subnetScopeInfo.NumPendingOffers) + } + + subnetReservationCount, err := dhcpV4EnumSubnetReservations(subnet.SubnetAddress) + if err != nil { + return fmt.Errorf("failed to get subnet reservation count: %w", err) + } else { + scope.ReservedAddress = float64(subnetReservationCount) + } + + var subnetStatistics *DHCP_FAILOVER_STATISTICS + err = dhcpV4FailoverGetScopeStatistics(subnet.SubnetAddress, &subnetStatistics) + + defer dhcpRpcFreeMemory(unsafe.Pointer(&subnetStatistics)) + + if err == nil { + scope.AddressesFree = float64(subnetStatistics.AddrFree) + scope.AddressesInUse = float64(subnetStatistics.AddrInUse) + scope.AddressesFreeOnPartnerServer = float64(subnetStatistics.PartnerAddrFree) + scope.AddressesInUseOnPartnerServer = float64(subnetStatistics.PartnerAddrInUse) + scope.AddressesFreeOnThisServer = float64(subnetStatistics.ThisAddrFree) + scope.AddressesInUseOnThisServer = float64(subnetStatistics.ThisAddrInUse) + } else if !errors.Is(err, ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP) { + return fmt.Errorf("failed to get subnet statistics: %w", err) + } + + scopes = append(scopes, scope) + + return nil + })(); err != nil { + errs = append(errs, err) + } + } + + return scopes, errors.Join(errs...) +} + +// dhcpGetSubnetInfo https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpgetsubnetinfo +func dhcpGetSubnetInfo(subnetAddress DHCP_IP_ADDRESS, subnetInfo **DHCP_SUBNET_INFO) error { + ret, _, _ := procDhcpGetSubnetInfo.Call( + 0, + uintptr(subnetAddress), + uintptr(unsafe.Pointer(subnetInfo)), + ) + + if ret != 0 { + return fmt.Errorf("dhcpGetSubnetInfo failed with code %w", syscall.Errno(ret)) + } + + return nil +} + +// dhcpV4FailoverGetScopeStatistics https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpv4failovergetscopestatistics +func dhcpV4FailoverGetScopeStatistics(scopeId DHCP_IP_ADDRESS, stats **DHCP_FAILOVER_STATISTICS) error { + ret, _, _ := procDhcpV4FailoverGetScopeStatistics.Call( + 0, + uintptr(scopeId), + uintptr(unsafe.Pointer(stats)), + ) + + if ret != 0 { + return fmt.Errorf("dhcpV4FailoverGetScopeStatistics failed with code %w", syscall.Errno(ret)) + } + + return nil +} + +// dhcpGetSuperScopeInfoV4 https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpgetsuperscopeinfov4 +func dhcpGetSuperScopeInfoV4(superScopeTable **DHCP_SUPER_SCOPE_TABLE) error { + ret, _, _ := procDhcpGetSuperScopeInfoV4.Call( + 0, + uintptr(unsafe.Pointer(superScopeTable)), + ) + + if ret != 0 { + return fmt.Errorf("dhcpGetSuperScopeInfoV4 failed with code %w", syscall.Errno(ret)) + } + + return nil +} + +// dhcpGetMibInfoV5 https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpgetmibinfov5 +func dhcpGetMibInfoV5(mibInfo **DHCP_MIB_INFO_V5) error { + ret, _, _ := procDhcpGetMibInfoV5.Call( + 0, + uintptr(unsafe.Pointer(mibInfo)), + ) + + if ret != 0 { + return fmt.Errorf("dhcpGetMibInfoV5 failed with code %w", syscall.Errno(ret)) + } + + return nil +} + +// dhcpV4EnumSubnetReservations https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpv4enumsubnetreservations +func dhcpV4EnumSubnetReservations(subnetAddress DHCP_IP_ADDRESS) (uint32, error) { + var elementsRead uint32 + var elementsTotal uint32 + var elementsInfo uintptr + var resumeHandle *windows.Handle + + ret, _, _ := procDhcpV4EnumSubnetReservations.Call( + 0, + uintptr(subnetAddress), + uintptr(unsafe.Pointer(&resumeHandle)), + 0, + uintptr(unsafe.Pointer(&elementsInfo)), + uintptr(unsafe.Pointer(&elementsRead)), + uintptr(unsafe.Pointer(&elementsTotal)), + ) + + dhcpRpcFreeMemory(unsafe.Pointer(&elementsInfo)) + + if !errors.Is(syscall.Errno(ret), windows.ERROR_MORE_DATA) && !errors.Is(syscall.Errno(ret), windows.ERROR_NO_MORE_ITEMS) { + return 0, fmt.Errorf("dhcpV4EnumSubnetReservations failed with code %w", syscall.Errno(ret)) + } + + return elementsRead + elementsTotal, nil +} + +func dhcpRpcFreeMemory(pointer unsafe.Pointer) { + if uintptr(pointer) == 0 { + return + } + + _, _, _ = procDhcpRpcFreeMemory.Call(uintptr(pointer)) +} diff --git a/internal/headers/dhcpsapi/dhcpsapi_test.go b/internal/headers/dhcpsapi/dhcpsapi_test.go new file mode 100644 index 000000000..d71183379 --- /dev/null +++ b/internal/headers/dhcpsapi/dhcpsapi_test.go @@ -0,0 +1,18 @@ +package dhcpsapi + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetDHCPV4ScopeStatistics(t *testing.T) { + t.Parallel() + + if procDhcpGetSuperScopeInfoV4.Find() != nil { + t.Skip("DhcpGetSuperScopeInfoV4 is not available") + } + + _, err := GetDHCPV4ScopeStatistics() + require.NoError(t, err) +} diff --git a/internal/headers/dhcpsapi/types.go b/internal/headers/dhcpsapi/types.go new file mode 100644 index 000000000..7d43ab8b2 --- /dev/null +++ b/internal/headers/dhcpsapi/types.go @@ -0,0 +1,135 @@ +package dhcpsapi + +import ( + "encoding/binary" + "net" + "syscall" + + "github.com/prometheus-community/windows_exporter/internal/headers/win32api" +) + +var ( + ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP = syscall.Errno(20116) +) + +type DHCPV4Scope struct { + Name string + State DHCP_SUBNET_STATE + SuperScopeName string + SuperScopeNumber uint32 + ScopeIPAddress net.IPNet + + AddressesFree float64 + AddressesFreeOnPartnerServer float64 + AddressesFreeOnThisServer float64 + AddressesInUse float64 + AddressesInUseOnPartnerServer float64 + AddressesInUseOnThisServer float64 + PendingOffers float64 + ReservedAddress float64 +} + +type DHCP_IP_ADDRESS win32api.DWORD +type DHCP_IP_MASK win32api.DWORD + +func (ip DHCP_IP_ADDRESS) IPv4() net.IP { + ipBytes := make([]byte, 4) + + binary.BigEndian.PutUint32(ipBytes, uint32(ip)) + + return ipBytes +} + +func (ip DHCP_IP_MASK) IPv4Mask() net.IPMask { + ipBytes := make([]byte, 4) + + binary.BigEndian.PutUint32(ipBytes, uint32(ip)) + + return ipBytes +} + +type DHCP_SUPER_SCOPE_TABLE struct { + Count win32api.DWORD + Entries *DHCP_SUPER_SCOPE_TABLE_ENTRY +} + +type DHCP_SUPER_SCOPE_TABLE_ENTRY struct { + SubnetAddress DHCP_IP_ADDRESS + SuperScopeNumber win32api.DWORD + NextInSuperScope win32api.DWORD + SuperScopeName win32api.LPWSTR +} + +// DHCP_SUBNET_INFO https://learn.microsoft.com/de-de/windows/win32/api/dhcpsapi/ns-dhcpsapi-dhcp_subnet_info +type DHCP_SUBNET_INFO struct { + SubnetAddress DHCP_IP_ADDRESS + SubnetMask DHCP_IP_MASK + SubnetName win32api.LPWSTR + SubnetComment win32api.LPWSTR + PrimaryHost DHCP_HOST_INFO + SubnetState DHCP_SUBNET_STATE +} + +type DHCP_HOST_INFO struct { + IpAddress DHCP_IP_ADDRESS + NetBiosName win32api.LPWSTR + HostName win32api.LPWSTR +} + +// DHCP_SUBNET_STATE https://learn.microsoft.com/de-de/windows/win32/api/dhcpsapi/ne-dhcpsapi-dhcp_subnet_state +type DHCP_SUBNET_STATE uint32 + +const ( + DhcpSubnetEnabled DHCP_SUBNET_STATE = 0 + DhcpSubnetDisabled DHCP_SUBNET_STATE = 1 + DhcpSubnetEnabledSwitched DHCP_SUBNET_STATE = 2 + DhcpSubnetDisabledSwitched DHCP_SUBNET_STATE = 3 + DhcpSubnetInvalidState DHCP_SUBNET_STATE = 4 +) + +var DHCP_SUBNET_STATE_NAMES = map[DHCP_SUBNET_STATE]string{ + DhcpSubnetEnabled: "Enabled", + DhcpSubnetDisabled: "Disabled", + DhcpSubnetEnabledSwitched: "EnabledSwitched", + DhcpSubnetDisabledSwitched: "DisabledSwitched", + DhcpSubnetInvalidState: "InvalidState", +} + +type DHCP_FAILOVER_STATISTICS struct { + NumAddr win32api.DWORD + AddrFree win32api.DWORD + AddrInUse win32api.DWORD + PartnerAddrFree win32api.DWORD + ThisAddrFree win32api.DWORD + PartnerAddrInUse win32api.DWORD + ThisAddrInUse win32api.DWORD +} + +type DHCP_MIB_INFO_V5 struct { + Discovers win32api.DWORD + Offers win32api.DWORD + Requests win32api.DWORD + Acks win32api.DWORD + Naks win32api.DWORD + Declines win32api.DWORD + Releases win32api.DWORD + ServerStartTime win32api.DATE_TIME + QtnNumLeases win32api.DWORD + QtnPctQtnLeases win32api.DWORD + QtnProbationLeases win32api.DWORD + QtnNonQtnLeases win32api.DWORD + QtnExemptLeases win32api.DWORD + QtnCapableClients win32api.DWORD + QtnIASErrors win32api.DWORD + DelayedOffers win32api.DWORD + ScopesWithDelayedOffers win32api.DWORD + Scopes win32api.DWORD + ScopeInfo *DHCP_SUBNET_MIB_INFO_V5 +} + +type DHCP_SUBNET_MIB_INFO_V5 struct { + Subnet DHCP_IP_ADDRESS + NumAddressesInUse win32api.DWORD + NumAddressesFree win32api.DWORD + NumPendingOffers win32api.DWORD +} diff --git a/internal/headers/win32api/types.go b/internal/headers/win32api/types.go new file mode 100644 index 000000000..50b5fb825 --- /dev/null +++ b/internal/headers/win32api/types.go @@ -0,0 +1,13 @@ +package win32api + +import "golang.org/x/sys/windows" + +type DATE_TIME = windows.Filetime +type DWORD = uint32 +type LPWSTR struct { + *uint16 +} + +func (s LPWSTR) String() string { + return windows.UTF16PtrToString(s.uint16) +} From a949567d44d07a42a51b889fb3382ccdb2426219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 18 Jan 2025 02:24:05 +0100 Subject: [PATCH 2/6] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/headers/dhcpsapi/dhcpsapi.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/headers/dhcpsapi/dhcpsapi.go b/internal/headers/dhcpsapi/dhcpsapi.go index 8270b165a..5e42d0853 100644 --- a/internal/headers/dhcpsapi/dhcpsapi.go +++ b/internal/headers/dhcpsapi/dhcpsapi.go @@ -28,7 +28,7 @@ func GetDHCPV4ScopeStatistics() ([]DHCPV4Scope, error) { return nil, err } - defer dhcpRpcFreeMemory(unsafe.Pointer(&mibInfo)) + defer dhcpRpcFreeMemory(unsafe.Pointer(mibInfo)) subnetScopeInfos := make(map[DHCP_IP_ADDRESS]DHCP_SUBNET_MIB_INFO_V5, mibInfo.Scopes) subnetMIBScopeInfos := unsafe.Slice(mibInfo.ScopeInfo, mibInfo.Scopes) @@ -45,7 +45,7 @@ func GetDHCPV4ScopeStatistics() ([]DHCPV4Scope, error) { return nil, errors.New("dhcpGetSuperScopeInfoV4 returned nil") } - defer dhcpRpcFreeMemory(unsafe.Pointer(&superScopeTable)) + defer dhcpRpcFreeMemory(unsafe.Pointer(superScopeTable)) scopes := make([]DHCPV4Scope, 0, superScopeTable.Count) subnets := unsafe.Slice(superScopeTable.Entries, superScopeTable.Count) @@ -60,7 +60,7 @@ func GetDHCPV4ScopeStatistics() ([]DHCPV4Scope, error) { return fmt.Errorf("failed to get subnet info: %w", err) } - defer dhcpRpcFreeMemory(unsafe.Pointer(&subnetInfo)) + defer dhcpRpcFreeMemory(unsafe.Pointer(subnetInfo)) scope := DHCPV4Scope{ Name: subnetInfo.SubnetName.String(), @@ -95,7 +95,7 @@ func GetDHCPV4ScopeStatistics() ([]DHCPV4Scope, error) { var subnetStatistics *DHCP_FAILOVER_STATISTICS err = dhcpV4FailoverGetScopeStatistics(subnet.SubnetAddress, &subnetStatistics) - defer dhcpRpcFreeMemory(unsafe.Pointer(&subnetStatistics)) + defer dhcpRpcFreeMemory(unsafe.Pointer(subnetStatistics)) if err == nil { scope.AddressesFree = float64(subnetStatistics.AddrFree) @@ -194,7 +194,7 @@ func dhcpV4EnumSubnetReservations(subnetAddress DHCP_IP_ADDRESS) (uint32, error) uintptr(unsafe.Pointer(&elementsTotal)), ) - dhcpRpcFreeMemory(unsafe.Pointer(&elementsInfo)) + dhcpRpcFreeMemory(unsafe.Pointer(elementsInfo)) if !errors.Is(syscall.Errno(ret), windows.ERROR_MORE_DATA) && !errors.Is(syscall.Errno(ret), windows.ERROR_NO_MORE_ITEMS) { return 0, fmt.Errorf("dhcpV4EnumSubnetReservations failed with code %w", syscall.Errno(ret)) From 6ba5f6c210596d6fe92f52c652939247691a574a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 18 Jan 2025 02:37:07 +0100 Subject: [PATCH 3/6] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- docs/collector.dhcp.md | 198 ++++++++++++++++++++++++++------ docs/collector.net.md | 4 + docs/collector.netframework.md | 4 +- internal/collector/dhcp/dhcp.go | 16 +-- 4 files changed, 178 insertions(+), 44 deletions(-) diff --git a/docs/collector.dhcp.md b/docs/collector.dhcp.md index 01d369a62..20eb15796 100644 --- a/docs/collector.dhcp.md +++ b/docs/collector.dhcp.md @@ -2,49 +2,177 @@ The dhcp collector exposes DHCP Server metrics -||| --|- -Metric name prefix | `dhcp` -Data source | Perflib -Classes | `DHCP Server` -Enabled by default? | No +| | | +|---------------------|---------------| +| Metric name prefix | `dhcp` | +| Data source | Perflib | +| Classes | `DHCP Server` | +| Enabled by default? | No | ## Flags -None +### `--collector.dhcp.enabled` + +Comma-separated list of collectors to use. Defaults to all, if not specified. ## Metrics -Name | Description | Type | Labels ------|-------------|------|------- -`packets_received_total` | Total number of packets received by the DHCP server | counter | None -`duplicates_dropped_total` | Total number of duplicate packets received by the DHCP server | counter | None -`packets_expired_total` | Total number of packets expired in the DHCP server message queue | counter | None -`active_queue_length` | Number of packets in the processing queue of the DHCP server | gauge | None -`conflict_check_queue_length` | Number of packets in the DHCP server queue waiting on conflict detection (ping) | gauge | None -`discovers_total` | Total DHCP Discovers received by the DHCP server | counter | None -`offers_total` | Total DHCP Offers sent by the DHCP server | counter | None -`requests_total` | Total DHCP Requests received by the DHCP server | counter | None -`informs_total` | Total DHCP Informs received by the DHCP server | counter | None -`acks_total` | Total DHCP Acks sent by the DHCP server | counter | None -`nacks_total` | Total DHCP Nacks sent by the DHCP server | counter | None -`declines_total` | Total DHCP Declines received by the DHCP server | counter | None -`releases_total` | Total DHCP Releases received by the DHCP server | counter | None -`offer_queue_length` | Number of packets in the offer queue of the DHCP server | gauge | None -`denied_due_to_match_total` | Total number of DHCP requests denied, based on matches from the Deny List | gauge | None -`denied_due_to_nonmatch_total` | Total number of DHCP requests denied, based on non-matches from the Allow List | gauge | None -`failover_bndupd_sent_total` | Number of DHCP failover Binding Update messages sent | counter | None -`failover_bndupd_received_total` | Number of DHCP failover Binding Update messages received | counter | None -`failover_bndack_sent_total` | Number of DHCP failover Binding Ack messages sent | counter | None -`failover_bndack_received_total` | Number of DHCP failover Binding Ack messages received | counter | None -`failover_bndupd_pending_in_outbound_queue` | Number of pending outbound DHCP failover Binding Update messages | counter | None -`failover_transitions_communicationinterrupted_state_total` | Total number of transitions into COMMUNICATION INTERRUPTED state | counter | None -`failover_transitions_partnerdown_state_total` | Total number of transitions into PARTNER DOWN state | counter | None -`failover_transitions_recover_total` | Total number of transitions into RECOVER state | counter | None -`failover_bndupd_dropped_total` | Total number of DHCP faileover Binding Updates dropped | counter | None +| Name | Description | Type | Labels | +|--------------------------------------------------------------------------|--------------------------------------------------------------------------------|---------|-----------------------------------------------------| +| `windows_dhcp_ack_total` | Total DHCP Acks sent by the DHCP server | counter | None | +| `windows_dhcp_denied_due_to_match_total` | Total number of DHCP requests denied, based on matches from the Deny List | gauge | None | +| `windows_dhcp_denied_due_to_nonmatch_total` | Total number of DHCP requests denied, based on non-matches from the Allow List | gauge | None | +| `windows_dhcp_declines_total` | Total DHCP Declines received by the DHCP server | counter | None | +| `windows_dhcp_discovers_total` | Total DHCP Discovers received by the DHCP server | counter | None | +| `windows_dhcp_failover_bndack_received_total` | Number of DHCP failover Binding Ack messages received | counter | None | +| `windows_dhcp_failover_bndack_sent_total` | Number of DHCP failover Binding Ack messages sent | counter | None | +| `windows_dhcp_failover_bndupd_dropped_total` | Total number of DHCP failover Binding Updates dropped | counter | None | +| `windows_dhcp_failover_bndupd_received_total` | Number of DHCP failover Binding Update messages received | counter | None | +| `windows_dhcp_failover_bndupd_sent_total` | Number of DHCP failover Binding Update messages sent | counter | None | +| `windows_dhcp_failover_bndupd_pending_in_outbound_queue` | Number of pending outbound DHCP failover Binding Update messages | counter | None | +| `windows_dhcp_failover_transitions_communicationinterrupted_state_total` | Total number of transitions into COMMUNICATION INTERRUPTED state | counter | None | +| `windows_dhcp_failover_transitions_partnerdown_state_total` | Total number of transitions into PARTNER DOWN state | counter | None | +| `windows_dhcp_failover_transitions_recover_total` | Total number of transitions into RECOVER state | counter | None | +| `windows_dhcp_informs_total` | Total DHCP Informs received by the DHCP server | counter | None | +| `windows_dhcp_nacks_total` | Total DHCP Nacks sent by the DHCP server | counter | None | +| `windows_dhcp_offers_total` | Total DHCP Offers sent by the DHCP server | counter | None | +| `windows_dhcp_packets_expired_total` | Total number of packets expired in the DHCP server message queue | counter | None | +| `windows_dhcp_packets_received_total` | Total number of packets received by the DHCP server | counter | None | +| `windows_dhcp_pending_offers_total` | Total number of pending offers in the DHCP server | counter | None | +| `windows_dhcp_releases_total` | Total DHCP Releases received by the DHCP server | counter | None | +| `windows_dhcp_requests_total` | Total DHCP Requests received by the DHCP server | counter | None | +| `windows_dhcp_scope_addresses_free_on_this_server` | DHCP Scope free addresses on this server | gauge | `scope` | +| `windows_dhcp_scope_addresses_free_on_partner_server` | DHCP Scope free addresses on partner server | gauge | `scope` | +| `windows_dhcp_scope_addresses_free` | DHCP Scope free addresses | gauge | `scope` | +| `windows_dhcp_scope_addresses_in_use_on_this_server` | DHCP Scope addresses in use on this server | gauge | `scope` | +| `windows_dhcp_scope_addresses_in_use_on_partner_server` | DHCP Scope addresses in use on partner server | gauge | `scope` | +| `windows_dhcp_scope_addresses_in_use` | DHCP Scope addresses in use | gauge | `scope` | +| `windows_dhcp_scope_info` | DHCP Scope information | gauge | `name`, `superscope_name`, `superscope_id`, `scope` | +| `windows_dhcp_scope_pending_offers` | DHCP Scope pending offers | gauge | `scope` | +| `windows_dhcp_scope_reserved_address` | DHCP Scope reserved addresses | gauge | `scope` | +| `windows_dhcp_scope_state` | DHCP Scope state | gauge | `scope`, `state` | + ### Example metric -_This collector does not yet have explained examples, we would appreciate your help adding them!_ +``` +# HELP windows_dhcp_acks_total Total DHCP Acks sent by the DHCP server (AcksTotal) +# TYPE windows_dhcp_acks_total counter +windows_dhcp_acks_total 0 +# HELP windows_dhcp_active_queue_length Number of packets in the processing queue of the DHCP server (ActiveQueueLength) +# TYPE windows_dhcp_active_queue_length gauge +windows_dhcp_active_queue_length 0 +# HELP windows_dhcp_conflict_check_queue_length Number of packets in the DHCP server queue waiting on conflict detection (ping). (ConflictCheckQueueLength) +# TYPE windows_dhcp_conflict_check_queue_length gauge +windows_dhcp_conflict_check_queue_length 0 +# HELP windows_dhcp_declines_total Total DHCP Declines received by the DHCP server (DeclinesTotal) +# TYPE windows_dhcp_declines_total counter +windows_dhcp_declines_total 0 +# HELP windows_dhcp_denied_due_to_match_total Total number of DHCP requests denied, based on matches from the Deny list (DeniedDueToMatch) +# TYPE windows_dhcp_denied_due_to_match_total counter +windows_dhcp_denied_due_to_match_total 0 +# HELP windows_dhcp_denied_due_to_nonmatch_total Total number of DHCP requests denied, based on non-matches from the Allow list (DeniedDueToNonMatch) +# TYPE windows_dhcp_denied_due_to_nonmatch_total counter +windows_dhcp_denied_due_to_nonmatch_total 0 +# HELP windows_dhcp_discovers_total Total DHCP Discovers received by the DHCP server (DiscoversTotal) +# TYPE windows_dhcp_discovers_total counter +windows_dhcp_discovers_total 0 +# HELP windows_dhcp_duplicates_dropped_total Total number of duplicate packets received by the DHCP server (DuplicatesDroppedTotal) +# TYPE windows_dhcp_duplicates_dropped_total counter +windows_dhcp_duplicates_dropped_total 0 +# HELP windows_dhcp_failover_bndack_received_total Number of DHCP fail over Binding Ack messages received (FailoverBndackReceivedTotal) +# TYPE windows_dhcp_failover_bndack_received_total counter +windows_dhcp_failover_bndack_received_total 0 +# HELP windows_dhcp_failover_bndack_sent_total Number of DHCP fail over Binding Ack messages sent (FailoverBndackSentTotal) +# TYPE windows_dhcp_failover_bndack_sent_total counter +windows_dhcp_failover_bndack_sent_total 0 +# HELP windows_dhcp_failover_bndupd_dropped_total Total number of DHCP fail over Binding Updates dropped (FailoverBndupdDropped) +# TYPE windows_dhcp_failover_bndupd_dropped_total counter +windows_dhcp_failover_bndupd_dropped_total 0 +# HELP windows_dhcp_failover_bndupd_pending_in_outbound_queue Number of pending outbound DHCP fail over Binding Update messages (FailoverBndupdPendingOutboundQueue) +# TYPE windows_dhcp_failover_bndupd_pending_in_outbound_queue gauge +windows_dhcp_failover_bndupd_pending_in_outbound_queue 0 +# HELP windows_dhcp_failover_bndupd_received_total Number of DHCP fail over Binding Update messages received (FailoverBndupdReceivedTotal) +# TYPE windows_dhcp_failover_bndupd_received_total counter +windows_dhcp_failover_bndupd_received_total 0 +# HELP windows_dhcp_failover_bndupd_sent_total Number of DHCP fail over Binding Update messages sent (FailoverBndupdSentTotal) +# TYPE windows_dhcp_failover_bndupd_sent_total counter +windows_dhcp_failover_bndupd_sent_total 0 +# HELP windows_dhcp_failover_transitions_communicationinterrupted_state_total Total number of transitions into COMMUNICATION INTERRUPTED state (FailoverTransitionsCommunicationinterruptedState) +# TYPE windows_dhcp_failover_transitions_communicationinterrupted_state_total counter +windows_dhcp_failover_transitions_communicationinterrupted_state_total 0 +# HELP windows_dhcp_failover_transitions_partnerdown_state_total Total number of transitions into PARTNER DOWN state (FailoverTransitionsPartnerdownState) +# TYPE windows_dhcp_failover_transitions_partnerdown_state_total counter +windows_dhcp_failover_transitions_partnerdown_state_total 0 +# HELP windows_dhcp_failover_transitions_recover_total Total number of transitions into RECOVER state (FailoverTransitionsRecoverState) +# TYPE windows_dhcp_failover_transitions_recover_total counter +windows_dhcp_failover_transitions_recover_total 0 +# HELP windows_dhcp_informs_total Total DHCP Informs received by the DHCP server (InformsTotal) +# TYPE windows_dhcp_informs_total counter +windows_dhcp_informs_total 0 +# HELP windows_dhcp_nacks_total Total DHCP Nacks sent by the DHCP server (NacksTotal) +# TYPE windows_dhcp_nacks_total counter +windows_dhcp_nacks_total 0 +# HELP windows_dhcp_offer_queue_length Number of packets in the offer queue of the DHCP server (OfferQueueLength) +# TYPE windows_dhcp_offer_queue_length gauge +windows_dhcp_offer_queue_length 0 +# HELP windows_dhcp_offers_total Total DHCP Offers sent by the DHCP server (OffersTotal) +# TYPE windows_dhcp_offers_total counter +windows_dhcp_offers_total 0 +# HELP windows_dhcp_packets_expired_total Total number of packets expired in the DHCP server message queue (PacketsExpiredTotal) +# TYPE windows_dhcp_packets_expired_total counter +windows_dhcp_packets_expired_total 0 +# HELP windows_dhcp_packets_received_total Total number of packets received by the DHCP server (PacketsReceivedTotal) +# TYPE windows_dhcp_packets_received_total counter +windows_dhcp_packets_received_total 0 +# HELP windows_dhcp_releases_total Total DHCP Releases received by the DHCP server (ReleasesTotal) +# TYPE windows_dhcp_releases_total counter +windows_dhcp_releases_total 0 +# HELP windows_dhcp_requests_total Total DHCP Requests received by the DHCP server (RequestsTotal) +# TYPE windows_dhcp_requests_total counter +windows_dhcp_requests_total 0 +# HELP windows_dhcp_scope_addresses_free_total DHCP Scope free addresses +# TYPE windows_dhcp_scope_addresses_free_total gauge +windows_dhcp_scope_addresses_free_total{scope="10.11.12.0/25"} 0 +windows_dhcp_scope_addresses_free_total{scope="172.16.0.0/24"} 0 +windows_dhcp_scope_addresses_free_total{scope="192.168.0.0/24"} 231 +# HELP windows_dhcp_scope_addresses_in_use_total DHCP Scope addresses in use +# TYPE windows_dhcp_scope_addresses_in_use_total gauge +windows_dhcp_scope_addresses_in_use_total{scope="10.11.12.0/25"} 0 +windows_dhcp_scope_addresses_in_use_total{scope="172.16.0.0/24"} 0 +windows_dhcp_scope_addresses_in_use_total{scope="192.168.0.0/24"} 0 +# HELP windows_dhcp_scope_info DHCP Scope information +# TYPE windows_dhcp_scope_info gauge +windows_dhcp_scope_info{name="SUBSUPERSCOPE",scope="172.16.0.0/24",superscope_id="2",superscope_name="SUPERSCOPE"} 1 +windows_dhcp_scope_info{name="TEST",scope="192.168.0.0/24",superscope_id="0",superscope_name=""} 1 +windows_dhcp_scope_info{name="TEST2",scope="10.11.12.0/25",superscope_id="2",superscope_name="SUPERSCOPE"} 1 +# HELP windows_dhcp_scope_pending_offers_total DHCP Scope pending offers +# TYPE windows_dhcp_scope_pending_offers_total gauge +windows_dhcp_scope_pending_offers_total{scope="10.11.12.0/25"} 0 +windows_dhcp_scope_pending_offers_total{scope="172.16.0.0/24"} 0 +windows_dhcp_scope_pending_offers_total{scope="192.168.0.0/24"} 0 +# HELP windows_dhcp_scope_reserved_address_total DHCP Scope reserved addresses +# TYPE windows_dhcp_scope_reserved_address_total gauge +windows_dhcp_scope_reserved_address_total{scope="10.11.12.0/25"} 0 +windows_dhcp_scope_reserved_address_total{scope="172.16.0.0/24"} 0 +windows_dhcp_scope_reserved_address_total{scope="192.168.0.0/24"} 2 +# HELP windows_dhcp_scope_state DHCP Scope state +# TYPE windows_dhcp_scope_state gauge +windows_dhcp_scope_state{scope="10.11.12.0/25",state="Disabled"} 1 +windows_dhcp_scope_state{scope="10.11.12.0/25",state="DisabledSwitched"} 0 +windows_dhcp_scope_state{scope="10.11.12.0/25",state="Enabled"} 0 +windows_dhcp_scope_state{scope="10.11.12.0/25",state="EnabledSwitched"} 0 +windows_dhcp_scope_state{scope="10.11.12.0/25",state="InvalidState"} 0 +windows_dhcp_scope_state{scope="172.16.0.0/24",state="Disabled"} 1 +windows_dhcp_scope_state{scope="172.16.0.0/24",state="DisabledSwitched"} 0 +windows_dhcp_scope_state{scope="172.16.0.0/24",state="Enabled"} 0 +windows_dhcp_scope_state{scope="172.16.0.0/24",state="EnabledSwitched"} 0 +windows_dhcp_scope_state{scope="172.16.0.0/24",state="InvalidState"} 0 +windows_dhcp_scope_state{scope="192.168.0.0/24",state="Disabled"} 0 +windows_dhcp_scope_state{scope="192.168.0.0/24",state="DisabledSwitched"} 0 +windows_dhcp_scope_state{scope="192.168.0.0/24",state="Enabled"} 1 +windows_dhcp_scope_state{scope="192.168.0.0/24",state="EnabledSwitched"} 0 +windows_dhcp_scope_state{scope="192.168.0.0/24",state="InvalidState"} 0 +``` ## Useful queries _This collector does not yet have any useful queries added, we would appreciate your help adding them!_ diff --git a/docs/collector.net.md b/docs/collector.net.md index 1006aaa7e..112c17186 100644 --- a/docs/collector.net.md +++ b/docs/collector.net.md @@ -19,6 +19,10 @@ If given, an interface name needs to match the include regexp in order for the c If given, an interface name needs to *not* match the exclude regexp in order for the corresponding metrics to be reported +### `--collector.net.enabled` + +Comma-separated list of collectors to use. Defaults to all, if not specified. + ## Metrics Name | Description | Type | Labels diff --git a/docs/collector.netframework.md b/docs/collector.netframework.md index 4ef4a3680..4b503dc65 100644 --- a/docs/collector.netframework.md +++ b/docs/collector.netframework.md @@ -10,7 +10,9 @@ The netframework collector exposes metrics about dotnet framework. ## Flags -None +### `--collector.netframework.enabled` + +Comma-separated list of collectors to use. Defaults to all, if not specified. ## Metrics diff --git a/internal/collector/dhcp/dhcp.go b/internal/collector/dhcp/dhcp.go index 0e5802b35..49991e64f 100644 --- a/internal/collector/dhcp/dhcp.go +++ b/internal/collector/dhcp/dhcp.go @@ -322,56 +322,56 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { ) c.scopeAddressesFreeTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free"), "DHCP Scope free addresses", []string{"scope"}, nil, ) c.scopeAddressesFreeOnPartnerServerTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_partner_server_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_partner_server"), "DHCP Scope free addresses on partner server", []string{"scope"}, nil, ) c.scopeAddressesFreeOnThisServerTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_this_server_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_this_server"), "DHCP Scope free addresses on this server", []string{"scope"}, nil, ) c.scopeAddressesInUseTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use"), "DHCP Scope addresses in use", []string{"scope"}, nil, ) c.scopeAddressesInUseOnPartnerServerTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_partner_server_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_partner_server"), "DHCP Scope addresses in use on partner server", []string{"scope"}, nil, ) c.scopeAddressesInUseOnThisServerTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_this_server_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_this_server"), "DHCP Scope addresses in use on this server", []string{"scope"}, nil, ) c.scopePendingOffersTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_pending_offers_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_pending_offers"), "DHCP Scope pending offers", []string{"scope"}, nil, ) c.scopeReservedAddressTotal = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "scope_reserved_address_total"), + prometheus.BuildFQName(types.Namespace, Name, "scope_reserved_address"), "DHCP Scope reserved addresses", []string{"scope"}, nil, From 83967fce165d6f333f9d0f4d1c67d4fca67f08e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 18 Jan 2025 02:47:32 +0100 Subject: [PATCH 4/6] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/headers/dhcpsapi/dhcpsapi.go | 24 +++++++++++++----------- internal/headers/dhcpsapi/types.go | 13 +++++++------ internal/headers/win32api/types.go | 12 +++++++----- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/internal/headers/dhcpsapi/dhcpsapi.go b/internal/headers/dhcpsapi/dhcpsapi.go index 5e42d0853..645f152e0 100644 --- a/internal/headers/dhcpsapi/dhcpsapi.go +++ b/internal/headers/dhcpsapi/dhcpsapi.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "net" - "syscall" "unsafe" "golang.org/x/sys/windows" @@ -128,7 +127,7 @@ func dhcpGetSubnetInfo(subnetAddress DHCP_IP_ADDRESS, subnetInfo **DHCP_SUBNET_I ) if ret != 0 { - return fmt.Errorf("dhcpGetSubnetInfo failed with code %w", syscall.Errno(ret)) + return fmt.Errorf("dhcpGetSubnetInfo failed with code %w", windows.Errno(ret)) } return nil @@ -143,7 +142,7 @@ func dhcpV4FailoverGetScopeStatistics(scopeId DHCP_IP_ADDRESS, stats **DHCP_FAIL ) if ret != 0 { - return fmt.Errorf("dhcpV4FailoverGetScopeStatistics failed with code %w", syscall.Errno(ret)) + return fmt.Errorf("dhcpV4FailoverGetScopeStatistics failed with code %w", windows.Errno(ret)) } return nil @@ -157,7 +156,7 @@ func dhcpGetSuperScopeInfoV4(superScopeTable **DHCP_SUPER_SCOPE_TABLE) error { ) if ret != 0 { - return fmt.Errorf("dhcpGetSuperScopeInfoV4 failed with code %w", syscall.Errno(ret)) + return fmt.Errorf("dhcpGetSuperScopeInfoV4 failed with code %w", windows.Errno(ret)) } return nil @@ -171,7 +170,7 @@ func dhcpGetMibInfoV5(mibInfo **DHCP_MIB_INFO_V5) error { ) if ret != 0 { - return fmt.Errorf("dhcpGetMibInfoV5 failed with code %w", syscall.Errno(ret)) + return fmt.Errorf("dhcpGetMibInfoV5 failed with code %w", windows.Errno(ret)) } return nil @@ -179,10 +178,12 @@ func dhcpGetMibInfoV5(mibInfo **DHCP_MIB_INFO_V5) error { // dhcpV4EnumSubnetReservations https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpv4enumsubnetreservations func dhcpV4EnumSubnetReservations(subnetAddress DHCP_IP_ADDRESS) (uint32, error) { - var elementsRead uint32 - var elementsTotal uint32 - var elementsInfo uintptr - var resumeHandle *windows.Handle + var ( + elementsRead uint32 + elementsTotal uint32 + elementsInfo uintptr + resumeHandle *windows.Handle + ) ret, _, _ := procDhcpV4EnumSubnetReservations.Call( 0, @@ -196,8 +197,8 @@ func dhcpV4EnumSubnetReservations(subnetAddress DHCP_IP_ADDRESS) (uint32, error) dhcpRpcFreeMemory(unsafe.Pointer(elementsInfo)) - if !errors.Is(syscall.Errno(ret), windows.ERROR_MORE_DATA) && !errors.Is(syscall.Errno(ret), windows.ERROR_NO_MORE_ITEMS) { - return 0, fmt.Errorf("dhcpV4EnumSubnetReservations failed with code %w", syscall.Errno(ret)) + if !errors.Is(windows.Errno(ret), windows.ERROR_MORE_DATA) && !errors.Is(windows.Errno(ret), windows.ERROR_NO_MORE_ITEMS) { + return 0, fmt.Errorf("dhcpV4EnumSubnetReservations failed with code %w", windows.Errno(ret)) } return elementsRead + elementsTotal, nil @@ -208,5 +209,6 @@ func dhcpRpcFreeMemory(pointer unsafe.Pointer) { return } + //nolint:dogsled _, _, _ = procDhcpRpcFreeMemory.Call(uintptr(pointer)) } diff --git a/internal/headers/dhcpsapi/types.go b/internal/headers/dhcpsapi/types.go index 7d43ab8b2..646f35859 100644 --- a/internal/headers/dhcpsapi/types.go +++ b/internal/headers/dhcpsapi/types.go @@ -3,14 +3,12 @@ package dhcpsapi import ( "encoding/binary" "net" - "syscall" "github.com/prometheus-community/windows_exporter/internal/headers/win32api" + "golang.org/x/sys/windows" ) -var ( - ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP = syscall.Errno(20116) -) +var ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP = windows.Errno(20116) type DHCPV4Scope struct { Name string @@ -29,8 +27,10 @@ type DHCPV4Scope struct { ReservedAddress float64 } -type DHCP_IP_ADDRESS win32api.DWORD -type DHCP_IP_MASK win32api.DWORD +type ( + DHCP_IP_ADDRESS win32api.DWORD + DHCP_IP_MASK win32api.DWORD +) func (ip DHCP_IP_ADDRESS) IPv4() net.IP { ipBytes := make([]byte, 4) @@ -87,6 +87,7 @@ const ( DhcpSubnetInvalidState DHCP_SUBNET_STATE = 4 ) +//nolint:gochecknoglobals var DHCP_SUBNET_STATE_NAMES = map[DHCP_SUBNET_STATE]string{ DhcpSubnetEnabled: "Enabled", DhcpSubnetDisabled: "Disabled", diff --git a/internal/headers/win32api/types.go b/internal/headers/win32api/types.go index 50b5fb825..9d5129dbf 100644 --- a/internal/headers/win32api/types.go +++ b/internal/headers/win32api/types.go @@ -2,11 +2,13 @@ package win32api import "golang.org/x/sys/windows" -type DATE_TIME = windows.Filetime -type DWORD = uint32 -type LPWSTR struct { - *uint16 -} +type ( + DATE_TIME = windows.Filetime + DWORD = uint32 + LPWSTR struct { + *uint16 + } +) func (s LPWSTR) String() string { return windows.UTF16PtrToString(s.uint16) From e286a10c4008069f68d2751e721095723eeb2906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 18 Jan 2025 02:51:04 +0100 Subject: [PATCH 5/6] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/headers/dhcpsapi/dhcpsapi_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/headers/dhcpsapi/dhcpsapi_test.go b/internal/headers/dhcpsapi/dhcpsapi_test.go index d71183379..e89454ad9 100644 --- a/internal/headers/dhcpsapi/dhcpsapi_test.go +++ b/internal/headers/dhcpsapi/dhcpsapi_test.go @@ -1,9 +1,11 @@ package dhcpsapi import ( + "errors" "testing" "github.com/stretchr/testify/require" + "golang.org/x/sys/windows" ) func TestGetDHCPV4ScopeStatistics(t *testing.T) { @@ -14,5 +16,9 @@ func TestGetDHCPV4ScopeStatistics(t *testing.T) { } _, err := GetDHCPV4ScopeStatistics() + if errors.Is(err, windows.Errno(1753)) { + t.Skip(err.Error()) + } + require.NoError(t, err) } From f50b8e68c481efeab8931151552e8df2e22eee29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 18 Jan 2025 02:56:25 +0100 Subject: [PATCH 6/6] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/headers/dhcpsapi/types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/headers/dhcpsapi/types.go b/internal/headers/dhcpsapi/types.go index 646f35859..45004b1ab 100644 --- a/internal/headers/dhcpsapi/types.go +++ b/internal/headers/dhcpsapi/types.go @@ -8,6 +8,7 @@ import ( "golang.org/x/sys/windows" ) +//nolint:gochecknoglobals var ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP = windows.Errno(20116) type DHCPV4Scope struct {