github.com/influxdata/telegraf@v1.30.3/internal/snmp/wrapper.go (about) 1 package snmp 2 3 import ( 4 "errors" 5 "fmt" 6 "net/url" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/gosnmp/gosnmp" 12 ) 13 14 // Connection is an interface which wraps a *gosnmp.GoSNMP object. 15 // We interact through an interface so we can mock it out in tests. 16 type Connection interface { 17 Host() string 18 //BulkWalkAll(string) ([]gosnmp.SnmpPDU, error) 19 Walk(string, gosnmp.WalkFunc) error 20 Get(oids []string) (*gosnmp.SnmpPacket, error) 21 Reconnect() error 22 } 23 24 // GosnmpWrapper wraps a *gosnmp.GoSNMP object so we can use it as a snmpConnection. 25 type GosnmpWrapper struct { 26 *gosnmp.GoSNMP 27 } 28 29 // Host returns the value of GoSNMP.Target. 30 func (gs GosnmpWrapper) Host() string { 31 return gs.Target 32 } 33 34 // Walk wraps GoSNMP.Walk() or GoSNMP.BulkWalk(), depending on whether the 35 // connection is using SNMPv1 or newer. 36 func (gs GosnmpWrapper) Walk(oid string, fn gosnmp.WalkFunc) error { 37 if gs.Version == gosnmp.Version1 { 38 return gs.GoSNMP.Walk(oid, fn) 39 } 40 return gs.GoSNMP.BulkWalk(oid, fn) 41 } 42 43 func NewWrapper(s ClientConfig) (GosnmpWrapper, error) { 44 gs := GosnmpWrapper{&gosnmp.GoSNMP{}} 45 46 gs.Timeout = time.Duration(s.Timeout) 47 48 gs.Retries = s.Retries 49 50 gs.UseUnconnectedUDPSocket = s.UnconnectedUDPSocket 51 52 switch s.Version { 53 case 3: 54 gs.Version = gosnmp.Version3 55 case 2, 0: 56 gs.Version = gosnmp.Version2c 57 case 1: 58 gs.Version = gosnmp.Version1 59 default: 60 return GosnmpWrapper{}, errors.New("invalid version") 61 } 62 63 if s.Version < 3 { 64 if s.Community == "" { 65 gs.Community = "public" 66 } else { 67 gs.Community = s.Community 68 } 69 } 70 71 gs.MaxRepetitions = s.MaxRepetitions 72 73 if s.Version == 3 { 74 gs.ContextName = s.ContextName 75 76 sp := &gosnmp.UsmSecurityParameters{} 77 gs.SecurityParameters = sp 78 gs.SecurityModel = gosnmp.UserSecurityModel 79 80 switch strings.ToLower(s.SecLevel) { 81 case "noauthnopriv", "": 82 gs.MsgFlags = gosnmp.NoAuthNoPriv 83 case "authnopriv": 84 gs.MsgFlags = gosnmp.AuthNoPriv 85 case "authpriv": 86 gs.MsgFlags = gosnmp.AuthPriv 87 default: 88 return GosnmpWrapper{}, errors.New("invalid secLevel") 89 } 90 91 sp.UserName = s.SecName 92 93 switch strings.ToLower(s.AuthProtocol) { 94 case "md5": 95 sp.AuthenticationProtocol = gosnmp.MD5 96 case "sha": 97 sp.AuthenticationProtocol = gosnmp.SHA 98 case "sha224": 99 sp.AuthenticationProtocol = gosnmp.SHA224 100 case "sha256": 101 sp.AuthenticationProtocol = gosnmp.SHA256 102 case "sha384": 103 sp.AuthenticationProtocol = gosnmp.SHA384 104 case "sha512": 105 sp.AuthenticationProtocol = gosnmp.SHA512 106 case "": 107 sp.AuthenticationProtocol = gosnmp.NoAuth 108 default: 109 return GosnmpWrapper{}, errors.New("invalid authProtocol") 110 } 111 112 sp.AuthenticationPassphrase = s.AuthPassword 113 114 switch strings.ToLower(s.PrivProtocol) { 115 case "des": 116 sp.PrivacyProtocol = gosnmp.DES 117 case "aes": 118 sp.PrivacyProtocol = gosnmp.AES 119 case "aes192": 120 sp.PrivacyProtocol = gosnmp.AES192 121 case "aes192c": 122 sp.PrivacyProtocol = gosnmp.AES192C 123 case "aes256": 124 sp.PrivacyProtocol = gosnmp.AES256 125 case "aes256c": 126 sp.PrivacyProtocol = gosnmp.AES256C 127 case "": 128 sp.PrivacyProtocol = gosnmp.NoPriv 129 default: 130 return GosnmpWrapper{}, errors.New("invalid privProtocol") 131 } 132 133 sp.PrivacyPassphrase = s.PrivPassword 134 135 sp.AuthoritativeEngineID = s.EngineID 136 137 sp.AuthoritativeEngineBoots = s.EngineBoots 138 139 sp.AuthoritativeEngineTime = s.EngineTime 140 } 141 return gs, nil 142 } 143 144 // SetAgent takes a url (scheme://host:port) and sets the wrapped 145 // GoSNMP struct's corresponding fields. This shouldn't be called 146 // after using the wrapped GoSNMP struct, for example after 147 // connecting. 148 func (gs *GosnmpWrapper) SetAgent(agent string) error { 149 if !strings.Contains(agent, "://") { 150 agent = "udp://" + agent 151 } 152 153 u, err := url.Parse(agent) 154 if err != nil { 155 return err 156 } 157 158 // Only allow udp{4,6} and tcp{4,6}. 159 // Allowing ip{4,6} does not make sense as specifying a port 160 // requires the specification of a protocol. 161 // gosnmp does not handle these errors well, which is why 162 // they can result in cryptic errors by net.Dial. 163 switch u.Scheme { 164 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 165 gs.Transport = u.Scheme 166 default: 167 return fmt.Errorf("unsupported scheme: %v", u.Scheme) 168 } 169 170 gs.Target = u.Hostname() 171 172 portStr := u.Port() 173 if portStr == "" { 174 portStr = "161" 175 } 176 port, err := strconv.ParseUint(portStr, 10, 16) 177 if err != nil { 178 return fmt.Errorf("parsing port: %w", err) 179 } 180 gs.Port = uint16(port) 181 return nil 182 } 183 184 func (gs GosnmpWrapper) Reconnect() error { 185 if gs.Conn == nil { 186 return gs.Connect() 187 } 188 189 return nil 190 }