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  }