2021-12-01 06:47:50 +08:00
|
|
|
package snmp
|
|
|
|
|
|
|
|
|
|
import (
|
2024-02-09 01:32:30 +08:00
|
|
|
"errors"
|
2021-12-01 06:47:50 +08:00
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
"github.com/sleepinggenius2/gosmi"
|
|
|
|
|
"github.com/sleepinggenius2/gosmi/types"
|
2022-10-26 18:06:08 +08:00
|
|
|
|
|
|
|
|
"github.com/influxdata/telegraf"
|
2021-12-01 06:47:50 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// must init, append path for each directory, load module for every file
|
|
|
|
|
// or gosmi will fail without saying why
|
|
|
|
|
var m sync.Mutex
|
2021-12-02 01:02:38 +08:00
|
|
|
var once sync.Once
|
2021-12-08 06:05:33 +08:00
|
|
|
var cache = make(map[string]bool)
|
2021-12-01 06:47:50 +08:00
|
|
|
|
2022-02-17 03:25:20 +08:00
|
|
|
type MibLoader interface {
|
2022-03-11 23:29:16 +08:00
|
|
|
// appendPath takes the path of a directory
|
2022-02-17 03:25:20 +08:00
|
|
|
appendPath(path string)
|
2022-03-11 23:29:16 +08:00
|
|
|
|
|
|
|
|
// loadModule takes the name of a file in one of the
|
|
|
|
|
// directories. Basename only, no relative or absolute path
|
|
|
|
|
loadModule(path string) error
|
2022-02-17 03:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type GosmiMibLoader struct{}
|
|
|
|
|
|
|
|
|
|
func (*GosmiMibLoader) appendPath(path string) {
|
2021-12-01 06:47:50 +08:00
|
|
|
m.Lock()
|
|
|
|
|
defer m.Unlock()
|
2021-12-08 06:05:33 +08:00
|
|
|
|
|
|
|
|
gosmi.AppendPath(path)
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-17 03:25:20 +08:00
|
|
|
func (*GosmiMibLoader) loadModule(path string) error {
|
2021-12-08 06:05:33 +08:00
|
|
|
m.Lock()
|
|
|
|
|
defer m.Unlock()
|
|
|
|
|
|
|
|
|
|
_, err := gosmi.LoadModule(path)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 02:49:36 +08:00
|
|
|
// will give all found folders to gosmi and load in all modules found in the folders
|
2022-02-17 03:25:20 +08:00
|
|
|
func LoadMibsFromPath(paths []string, log telegraf.Logger, loader MibLoader) error {
|
|
|
|
|
folders, err := walkPaths(paths, log)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, path := range folders {
|
|
|
|
|
loader.appendPath(path)
|
2022-09-20 23:37:14 +08:00
|
|
|
modules, err := os.ReadDir(path)
|
2022-02-17 03:25:20 +08:00
|
|
|
if err != nil {
|
|
|
|
|
log.Warnf("Can't read directory %v", modules)
|
2022-09-20 23:37:14 +08:00
|
|
|
continue
|
2022-02-17 03:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-20 23:37:14 +08:00
|
|
|
for _, entry := range modules {
|
|
|
|
|
info, err := entry.Info()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Warnf("Couldn't get info for %v: %v", entry.Name(), err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-02-17 03:25:20 +08:00
|
|
|
if info.Mode()&os.ModeSymlink != 0 {
|
2022-03-11 23:29:16 +08:00
|
|
|
symlink := filepath.Join(path, info.Name())
|
|
|
|
|
target, err := filepath.EvalSymlinks(symlink)
|
2022-02-17 03:25:20 +08:00
|
|
|
if err != nil {
|
2022-03-11 23:29:16 +08:00
|
|
|
log.Warnf("Couldn't evaluate symbolic links for %v: %v", symlink, err)
|
2022-02-17 03:25:20 +08:00
|
|
|
continue
|
|
|
|
|
}
|
2022-03-11 23:29:16 +08:00
|
|
|
//replace symlink's info with the target's info
|
|
|
|
|
info, err = os.Lstat(target)
|
2022-02-17 03:25:20 +08:00
|
|
|
if err != nil {
|
2022-03-01 02:37:08 +08:00
|
|
|
log.Warnf("Couldn't stat target %v: %v", target, err)
|
2022-02-17 03:25:20 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if info.Mode().IsRegular() {
|
|
|
|
|
err := loader.loadModule(info.Name())
|
|
|
|
|
if err != nil {
|
2022-03-01 02:37:08 +08:00
|
|
|
log.Warnf("Couldn't load module %v: %v", info.Name(), err)
|
2022-02-17 03:25:20 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 02:49:36 +08:00
|
|
|
// should walk the paths given and find all folders
|
2022-02-17 03:25:20 +08:00
|
|
|
func walkPaths(paths []string, log telegraf.Logger) ([]string, error) {
|
2021-12-02 01:02:38 +08:00
|
|
|
once.Do(gosmi.Init)
|
2022-02-17 03:25:20 +08:00
|
|
|
folders := []string{}
|
2021-12-08 06:05:33 +08:00
|
|
|
|
2021-12-01 06:47:50 +08:00
|
|
|
for _, mibPath := range paths {
|
2021-12-08 06:05:33 +08:00
|
|
|
// Check if we loaded that path already and skip it if so
|
|
|
|
|
m.Lock()
|
|
|
|
|
cached := cache[mibPath]
|
|
|
|
|
cache[mibPath] = true
|
|
|
|
|
m.Unlock()
|
|
|
|
|
if cached {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-01 06:47:50 +08:00
|
|
|
err := filepath.Walk(mibPath, func(path string, info os.FileInfo, err error) error {
|
2021-12-18 05:30:08 +08:00
|
|
|
if info == nil {
|
2022-01-12 06:17:05 +08:00
|
|
|
log.Warnf("No mibs found")
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
log.Warnf("MIB path doesn't exist: %q", mibPath)
|
|
|
|
|
} else if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2021-12-18 05:30:08 +08:00
|
|
|
}
|
2022-02-17 03:25:20 +08:00
|
|
|
|
2021-12-01 06:47:50 +08:00
|
|
|
if info.Mode()&os.ModeSymlink != 0 {
|
2022-02-17 03:25:20 +08:00
|
|
|
target, err := filepath.EvalSymlinks(path)
|
2021-12-01 06:47:50 +08:00
|
|
|
if err != nil {
|
2022-03-11 23:29:16 +08:00
|
|
|
log.Warnf("Couldn't evaluate symbolic links for %v: %v", path, err)
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
2022-02-17 03:25:20 +08:00
|
|
|
info, err = os.Lstat(target)
|
|
|
|
|
if err != nil {
|
2022-03-01 02:37:08 +08:00
|
|
|
log.Warnf("Couldn't stat target %v: %v", target, err)
|
2022-02-17 03:25:20 +08:00
|
|
|
}
|
|
|
|
|
path = target
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
2022-02-17 03:25:20 +08:00
|
|
|
if info.IsDir() {
|
|
|
|
|
folders = append(folders, path)
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-01 06:47:50 +08:00
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
2023-02-22 19:57:53 +08:00
|
|
|
return folders, fmt.Errorf("couldn't walk path %q: %w", mibPath, err)
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
}
|
2022-02-17 03:25:20 +08:00
|
|
|
return folders, nil
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The following is for snmp_trap
|
|
|
|
|
type MibEntry struct {
|
|
|
|
|
MibName string
|
|
|
|
|
OidText string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TrapLookup(oid string) (e MibEntry, err error) {
|
2022-01-28 05:38:03 +08:00
|
|
|
var givenOid types.Oid
|
|
|
|
|
if givenOid, err = types.OidFromString(oid); err != nil {
|
|
|
|
|
return e, fmt.Errorf("could not convert OID %s: %w", oid, err)
|
|
|
|
|
}
|
2021-12-01 06:47:50 +08:00
|
|
|
|
2022-01-28 05:38:03 +08:00
|
|
|
// Get node name
|
|
|
|
|
var node gosmi.SmiNode
|
|
|
|
|
if node, err = gosmi.GetNodeByOID(givenOid); err != nil {
|
2021-12-01 06:47:50 +08:00
|
|
|
return e, err
|
|
|
|
|
}
|
2022-01-28 05:38:03 +08:00
|
|
|
e.OidText = node.Name
|
2021-12-01 06:47:50 +08:00
|
|
|
|
2022-01-28 05:38:03 +08:00
|
|
|
// Add not found OID part
|
|
|
|
|
if !givenOid.Equals(node.Oid) {
|
|
|
|
|
e.OidText += "." + givenOid[len(node.Oid):].String()
|
|
|
|
|
}
|
2021-12-01 06:47:50 +08:00
|
|
|
|
2022-01-28 05:38:03 +08:00
|
|
|
// Get module name
|
|
|
|
|
module := node.GetModule()
|
|
|
|
|
if module.Name != "<well-known>" {
|
|
|
|
|
e.MibName = module.Name
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
2022-01-28 05:38:03 +08:00
|
|
|
|
2021-12-01 06:47:50 +08:00
|
|
|
return e, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The following is for snmp
|
|
|
|
|
|
2022-11-09 03:04:12 +08:00
|
|
|
func GetIndex(mibPrefix string, node gosmi.SmiNode) (col []string, tagOids map[string]struct{}) {
|
2021-12-01 06:47:50 +08:00
|
|
|
// first attempt to get the table's tags
|
|
|
|
|
tagOids = map[string]struct{}{}
|
|
|
|
|
|
|
|
|
|
// mimcks grabbing INDEX {} that is returned from snmptranslate -Td MibName
|
|
|
|
|
for _, index := range node.GetIndex() {
|
|
|
|
|
tagOids[mibPrefix+index.Name] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// grabs all columns from the table
|
|
|
|
|
// mimmicks grabbing everything returned from snmptable -Ch -Cl -c public 127.0.0.1 oidFullName
|
2021-12-21 03:49:03 +08:00
|
|
|
_, col = node.GetColumns()
|
2021-12-01 06:47:50 +08:00
|
|
|
|
2022-11-09 03:04:12 +08:00
|
|
|
return col, tagOids
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//nolint:revive //Too many return variable but necessary
|
2021-12-21 04:40:00 +08:00
|
|
|
func SnmpTranslateCall(oid string) (mibName string, oidNum string, oidText string, conversion string, node gosmi.SmiNode, err error) {
|
2021-12-01 06:47:50 +08:00
|
|
|
var out gosmi.SmiNode
|
|
|
|
|
var end string
|
|
|
|
|
if strings.ContainsAny(oid, "::") {
|
|
|
|
|
// split given oid
|
|
|
|
|
// for example RFC1213-MIB::sysUpTime.0
|
2021-12-21 01:52:47 +08:00
|
|
|
s := strings.SplitN(oid, "::", 2)
|
2021-12-21 04:40:00 +08:00
|
|
|
// moduleName becomes RFC1213
|
|
|
|
|
moduleName := s[0]
|
|
|
|
|
module, err := gosmi.GetModule(moduleName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return oid, oid, oid, oid, gosmi.SmiNode{}, err
|
|
|
|
|
}
|
2021-12-21 01:52:47 +08:00
|
|
|
if s[1] == "" {
|
2022-10-26 18:06:08 +08:00
|
|
|
return "", oid, oid, oid, gosmi.SmiNode{}, fmt.Errorf("cannot parse %v", oid)
|
2021-12-21 01:52:47 +08:00
|
|
|
}
|
2021-12-21 04:40:00 +08:00
|
|
|
// node becomes sysUpTime.0
|
2021-12-01 06:47:50 +08:00
|
|
|
node := s[1]
|
|
|
|
|
if strings.ContainsAny(node, ".") {
|
2021-12-21 01:52:47 +08:00
|
|
|
s = strings.SplitN(node, ".", 2)
|
2021-12-01 06:47:50 +08:00
|
|
|
// node becomes sysUpTime
|
|
|
|
|
node = s[0]
|
|
|
|
|
end = "." + s[1]
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-21 04:40:00 +08:00
|
|
|
out, err = module.GetNode(node)
|
2021-12-01 06:47:50 +08:00
|
|
|
if err != nil {
|
2021-12-21 04:40:00 +08:00
|
|
|
return oid, oid, oid, oid, out, err
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
|
2021-12-23 04:41:34 +08:00
|
|
|
if oidNum = out.RenderNumeric(); oidNum == "" {
|
2024-01-23 22:54:16 +08:00
|
|
|
return oid, oid, oid, oid, out, fmt.Errorf("cannot translate %v into a numeric OID, please ensure all imported MIBs are in the path", oid)
|
2021-12-23 04:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oidNum = "." + oidNum + end
|
2021-12-01 06:47:50 +08:00
|
|
|
} else if strings.ContainsAny(oid, "abcdefghijklnmopqrstuvwxyz") {
|
|
|
|
|
//handle mixed oid ex. .iso.2.3
|
|
|
|
|
s := strings.Split(oid, ".")
|
|
|
|
|
for i := range s {
|
|
|
|
|
if strings.ContainsAny(s[i], "abcdefghijklmnopqrstuvwxyz") {
|
|
|
|
|
out, err = gosmi.GetNode(s[i])
|
|
|
|
|
if err != nil {
|
2021-12-21 04:40:00 +08:00
|
|
|
return oid, oid, oid, oid, out, err
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
s[i] = out.RenderNumeric()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
oidNum = strings.Join(s, ".")
|
|
|
|
|
out, _ = gosmi.GetNodeByOID(types.OidMustFromString(oidNum))
|
|
|
|
|
} else {
|
|
|
|
|
out, err = gosmi.GetNodeByOID(types.OidMustFromString(oid))
|
|
|
|
|
oidNum = oid
|
|
|
|
|
// ensure modules are loaded or node will be empty (might not error)
|
2023-01-26 04:28:13 +08:00
|
|
|
//nolint:nilerr // do not return the err as the oid is numeric and telegraf can continue
|
2021-12-01 06:47:50 +08:00
|
|
|
if err != nil || out.Name == "iso" {
|
2021-12-21 04:40:00 +08:00
|
|
|
return oid, oid, oid, oid, out, nil
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tc := out.GetSubtree()
|
|
|
|
|
|
|
|
|
|
for i := range tc {
|
|
|
|
|
// case where the mib doesn't have a conversion so Type struct will be nil
|
|
|
|
|
// prevents seg fault
|
|
|
|
|
if tc[i].Type == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
switch tc[i].Type.Name {
|
|
|
|
|
case "MacAddress", "PhysAddress":
|
|
|
|
|
conversion = "hwaddr"
|
|
|
|
|
case "InetAddressIPv4", "InetAddressIPv6", "InetAddress", "IPSIpAddress":
|
|
|
|
|
conversion = "ipaddr"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oidText = out.RenderQualified()
|
|
|
|
|
i := strings.Index(oidText, "::")
|
|
|
|
|
if i == -1 {
|
2024-02-09 01:32:30 +08:00
|
|
|
return "", oid, oid, oid, out, errors.New("not found")
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|
|
|
|
|
mibName = oidText[:i]
|
|
|
|
|
oidText = oidText[i+2:] + end
|
|
|
|
|
|
2021-12-21 04:40:00 +08:00
|
|
|
return mibName, oidNum, oidText, conversion, out, nil
|
2021-12-01 06:47:50 +08:00
|
|
|
}
|