master
go 150 lines 6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package snmpsd
4
5 import (
6 "fmt"
7
8 "github.com/gosnmp/gosnmp"
9
10 "github.com/netdata/netdata/go/plugins/pkg/confopt"
11 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/iprange"
12 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/snmputils"
13 )
14
15 type (
16 Config struct {
17 Source string `yaml:"-" json:"-"`
18
19 // RescanInterval defines how often to scan the networks for devices (default: 30m)
20 // Zero means use default. Negative means disable rescanning (run once).
21 RescanInterval confopt.LongDuration `yaml:"rescan_interval,omitempty" json:"rescan_interval,omitempty"`
22 // Timeout defines the maximum time to wait for SNMP device responses (default: 1s)
23 Timeout confopt.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
24 // DeviceCacheTTL defines how long to trust cached discovery results before requiring a new probe (default: 12h)
25 // Zero means use default. Negative means cache never expires.
26 DeviceCacheTTL confopt.LongDuration `yaml:"device_cache_ttl,omitempty" json:"device_cache_ttl,omitempty"`
27 // ParallelScansPerNetwork defines how many IPs to scan concurrently within each subnet (default: 32)
28 ParallelScansPerNetwork int `yaml:"parallel_scans_per_network,omitempty" json:"parallel_scans_per_network,omitempty"`
29 // Credentials define the SNMP credentials used for authentication
30 Credentials []CredentialConfig `yaml:"credentials,omitempty" json:"credentials,omitempty"`
31 // Networks defines the subnets to scan and which credentials to use
32 Networks []NetworkConfig `yaml:"networks,omitempty" json:"networks,omitempty"`
33 }
34
35 NetworkConfig struct {
36 // Subnet is the IP range to scan, supporting various formats
37 // https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/pkg/iprange#supported-formats
38 Subnet string `yaml:"subnet" json:"subnet"`
39 // Credential is the name of a credential from the Credentials list
40 Credential string `yaml:"credential" json:"credential"`
41 }
42 CredentialConfig struct {
43 // Name is the identifier for this credential set, used in Network.Credential
44 Name string `yaml:"name" json:"name"`
45 // Version must be one of: "1", "2c", or "3"
46 Version string `yaml:"version" json:"version"`
47 // Community is the SNMP community string (used in v1 and v2c)
48 Community string `yaml:"community,omitempty" json:"community,omitempty"`
49 // UserName is the SNMPv3 username
50 UserName string `yaml:"username,omitempty" json:"username,omitempty"`
51 // SecurityLevel must be one of: "noAuthNoPriv", "authNoPriv", or "authPriv" (for SNMPv3)
52 SecurityLevel string `yaml:"security_level,omitempty" json:"security_level,omitempty"`
53 // AuthProtocol must be one of: "md5", "sha", "sha224", "sha256", "sha384", "sha512" (for SNMPv3)
54 AuthProtocol string `yaml:"auth_protocol,omitempty" json:"auth_protocol,omitempty"`
55 // AuthPassphrase is the authentication passphrase (for SNMPv3)
56 AuthPassphrase string `yaml:"auth_password,omitempty" json:"auth_password,omitempty"`
57 // PrivacyProtocol must be one of: "des", "aes", "aes192", "aes256", "aes192c", "aes256c" (for SNMPv3)
58 PrivacyProtocol string `yaml:"priv_protocol,omitempty" json:"priv_protocol,omitempty"`
59 // PrivacyPassphrase is the privacy passphrase (for SNMPv3)
60 PrivacyPassphrase string `yaml:"priv_password,omitempty" json:"priv_password,omitempty"`
61 // ContextName is the SNMPv3 context name. Empty means the default context.
62 ContextName string `yaml:"context_name,omitempty" json:"context_name,omitempty"`
63 }
64 )
65
66 func (c *Config) validateAndParse() ([]subnet, error) {
67 if len(c.Credentials) == 0 {
68 return nil, fmt.Errorf("no credentials provided")
69 }
70 if len(c.Networks) == 0 {
71 return nil, fmt.Errorf("no networks provided")
72 }
73
74 credentials := make(map[string]CredentialConfig)
75
76 for i, cr := range c.Credentials {
77 if cr.Name == "" {
78 return nil, fmt.Errorf("no name provided for credential %d", i)
79 }
80 if _, ok := credentials[cr.Name]; ok {
81 return nil, fmt.Errorf("duplicate credential name: %s", cr.Name)
82 }
83 credentials[cr.Name] = c.Credentials[i]
84 }
85
86 networks := make(map[string]bool)
87
88 var subnets []subnet
89
90 for i, n := range c.Networks {
91 if n.Subnet == "" {
92 return nil, fmt.Errorf("no subnet provided for network %d", i)
93 }
94 if n.Credential == "" {
95 return nil, fmt.Errorf("no credential provided for network %s", n.Subnet)
96 }
97 if _, ok := credentials[n.Credential]; !ok {
98 return nil, fmt.Errorf("no credential provided for network %s", n.Subnet)
99 }
100
101 r, err := iprange.ParseRange(n.Subnet)
102 if err != nil {
103 return nil, fmt.Errorf("invalid subnet range '%s': %v", n.Subnet, err)
104 }
105
106 // Limit subnet size to /23 or smaller (512 IPs max per subnet)
107 // This prevents accidental scanning of excessively large networks.
108 if s := r.Size().Int64(); s > 512 {
109 return nil, fmt.Errorf("subnet '%s' exceeds maximum size of /23 (512 IPs, got %d IPs)", n.Subnet, s)
110 }
111
112 sub := subnet{
113 str: n.Subnet,
114 ips: r,
115 credential: credentials[n.Credential],
116 }
117
118 if networks[subKey(sub)] {
119 return nil, fmt.Errorf("duplicate subnet '%s'", subKey(sub))
120 }
121 networks[subKey(sub)] = true
122
123 subnets = append(subnets, sub)
124 }
125
126 return subnets, nil
127 }
128
129 func setCredential(client gosnmp.Handler, cred CredentialConfig) {
130 switch snmputils.ParseSNMPVersion(cred.Version) {
131 case gosnmp.Version1:
132 client.SetVersion(gosnmp.Version1)
133 client.SetCommunity(cred.Community)
134 case gosnmp.Version2c:
135 client.SetVersion(gosnmp.Version2c)
136 client.SetCommunity(cred.Community)
137 case gosnmp.Version3:
138 client.SetVersion(gosnmp.Version3)
139 client.SetSecurityModel(gosnmp.UserSecurityModel)
140 client.SetMsgFlags(snmputils.ParseSNMPv3SecurityLevel(cred.SecurityLevel))
141 client.SetSecurityParameters(&gosnmp.UsmSecurityParameters{
142 UserName: cred.UserName,
143 AuthenticationProtocol: snmputils.ParseSNMPv3AuthProtocol(cred.AuthProtocol),
144 AuthenticationPassphrase: cred.AuthPassphrase,
145 PrivacyProtocol: snmputils.ParseSNMPv3PrivProtocol(cred.PrivacyProtocol),
146 PrivacyPassphrase: cred.PrivacyPassphrase,
147 })
148 client.SetContextName(cred.ContextName)
149 }
150 }