gosnmp

Форк
0
/
gosnmp.go 
695 строк · 22.2 Кб
1
// Copyright 2012 The GoSNMP Authors. All rights reserved.  Use of this
2
// source code is governed by a BSD-style license that can be found in the
3
// LICENSE file.
4

5
// Copyright 2009 The Go Authors. All rights reserved.
6
// Use of this source code is governed by a BSD-style
7
// license that can be found in the LICENSE file.
8

9
package gosnmp
10

11
import (
12
	"context"
13
	"crypto/rand"
14
	"fmt"
15
	"math"
16
	"math/big"
17
	"net"
18
	"strconv"
19
	"sync/atomic"
20
	"syscall"
21
	"time"
22
)
23

24
const (
25
	// MaxOids is the maximum number of OIDs permitted in a single call,
26
	// otherwise error. MaxOids too high can cause remote devices to fail
27
	// strangely. 60 seems to be a common value that works, but you will want
28
	// to change this in the GoSNMP struct
29
	MaxOids = 60
30

31
	// Base OID for MIB-2 defined SNMP variables
32
	baseOid = ".1.3.6.1.2.1"
33

34
	// Max oid sub-identifier value
35
	// https://tools.ietf.org/html/rfc2578#section-7.1.3
36
	MaxObjectSubIdentifierValue = 4294967295
37

38
	// Java SNMP uses 50, snmp-net uses 10
39
	defaultMaxRepetitions = 50
40

41
	// "udp" and "tcp" are used regularly, prevent 'goconst' complaints
42
	udp = "udp"
43
	tcp = "tcp"
44
)
45

46
// GoSNMP represents GoSNMP library state.
47
type GoSNMP struct {
48
	// Conn is net connection to use, typically established using GoSNMP.Connect().
49
	Conn net.Conn
50

51
	// Target is an ipv4 address.
52
	Target string
53

54
	// Port is a port.
55
	Port uint16
56

57
	// Transport is the transport protocol to use ("udp" or "tcp"); if unset "udp" will be used.
58
	Transport string
59

60
	// Community is an SNMP Community string.
61
	Community string
62

63
	// Version is an SNMP Version.
64
	Version SnmpVersion
65

66
	// Context allows for overall deadlines and cancellation.
67
	Context context.Context
68

69
	// Timeout is the timeout for one SNMP request/response.
70
	Timeout time.Duration
71

72
	// Set the number of retries to attempt.
73
	Retries int
74

75
	// Double timeout in each retry.
76
	ExponentialTimeout bool
77

78
	// Logger is the GoSNMP.Logger to use for debugging.
79
	// For verbose logging to stdout:
80
	// x.Logger = NewLogger(log.New(os.Stdout, "", 0))
81
	// For Release builds, you can turn off logging entirely by using the go build tag "gosnmp_nodebug" even if the logger was installed.
82
	Logger Logger
83

84
	// Message hook methods allow passing in a functions at various points in the packet handling.
85
	// For example, this can be used to collect packet timing, add metrics, or implement tracing.
86
	/*
87

88
	 */
89
	// PreSend is called before a packet is sent.
90
	PreSend func(*GoSNMP)
91

92
	// OnSent is called when a packet is sent.
93
	OnSent func(*GoSNMP)
94

95
	// OnRecv is called when a packet is received.
96
	OnRecv func(*GoSNMP)
97

98
	// OnRetry is called when a retry attempt is done.
99
	OnRetry func(*GoSNMP)
100

101
	// OnFinish is called when the request completed.
102
	OnFinish func(*GoSNMP)
103

104
	// MaxOids is the maximum number of oids allowed in a Get().
105
	// (default: MaxOids)
106
	MaxOids int
107

108
	// MaxRepetitions sets the GETBULK max-repetitions used by BulkWalk*
109
	// Unless MaxRepetitions is specified it will use defaultMaxRepetitions (50)
110
	// This may cause issues with some devices, if so set MaxRepetitions lower.
111
	// See comments in https://github.com/gosnmp/gosnmp/issues/100
112
	MaxRepetitions uint32
113

114
	// NonRepeaters sets the GETBULK max-repeaters used by BulkWalk*.
115
	// (default: 0 as per RFC 1905)
116
	NonRepeaters int
117

118
	// UseUnconnectedUDPSocket if set, changes net.Conn to be unconnected UDP socket.
119
	// Some multi-homed network gear isn't smart enough to send SNMP responses
120
	// from the address it received the requests on. To work around that,
121
	// we open unconnected UDP socket and use sendto/recvfrom.
122
	UseUnconnectedUDPSocket bool
123

124
	// If Control is not nil, it is called after creating the network
125
	// connection but before actually dialing.
126
	//
127
	// Can be used when UseUnconnectedUDPSocket is set to false or when using TCP
128
	// in scenario where specific options on the underlying socket are nedded.
129
	// Refer to https://pkg.go.dev/net#Dialer
130
	Control func(network, address string, c syscall.RawConn) error
131

132
	// LocalAddr is the local address in the format "address:port" to use when connecting an Target address.
133
	// If the port parameter is empty or "0", as in
134
	// "127.0.0.1:" or "[::1]:0", a port number is automatically (random) chosen.
135
	LocalAddr string
136

137
	// netsnmp has '-C APPOPTS - set various application specific behaviours'
138
	//
139
	// - 'c: do not check returned OIDs are increasing' - use AppOpts = map[string]interface{"c":true} with
140
	//   Walk() or BulkWalk(). The library user needs to implement their own policy for terminating walks.
141
	// - 'p,i,I,t,E' -> pull requests welcome
142
	AppOpts map[string]interface{}
143

144
	// Internal - used to sync requests to responses.
145
	requestID uint32
146
	random    uint32
147

148
	rxBuf *[rxBufSize]byte // has to be pointer due to https://github.com/golang/go/issues/11728
149

150
	// MsgFlags is an SNMPV3 MsgFlags.
151
	MsgFlags SnmpV3MsgFlags
152

153
	// SecurityModel is an SNMPV3 Security Model.
154
	SecurityModel SnmpV3SecurityModel
155

156
	// SecurityParameters is an SNMPV3 Security Model parameters struct.
157
	SecurityParameters SnmpV3SecurityParameters
158

159
	// TrapSecurityParametersTable is a mapping of identifiers to corresponding SNMP V3 Security Model parameters
160
	// right now only supported for receiving traps, variable name to make that clear
161
	TrapSecurityParametersTable *SnmpV3SecurityParametersTable
162

163
	// ContextEngineID is SNMPV3 ContextEngineID in ScopedPDU.
164
	ContextEngineID string
165

166
	// ContextName is SNMPV3 ContextName in ScopedPDU
167
	ContextName string
168

169
	// Internal - used to sync requests to responses - snmpv3.
170
	msgID uint32
171

172
	// Internal - we use to send packets if using unconnected socket.
173
	uaddr *net.UDPAddr
174
}
175

176
// Default connection settings
177
//
178
//nolint:gochecknoglobals
179
var Default = &GoSNMP{
180
	Port:               161,
181
	Transport:          udp,
182
	Community:          "public",
183
	Version:            Version2c,
184
	Timeout:            time.Duration(2) * time.Second,
185
	Retries:            3,
186
	ExponentialTimeout: true,
187
	MaxOids:            MaxOids,
188
}
189

190
// SnmpPDU will be used when doing SNMP Set's
191
type SnmpPDU struct {
192
	// The value to be set by the SNMP set, or the value when
193
	// sending a trap
194
	Value interface{}
195

196
	// Name is an oid in string format eg ".1.3.6.1.4.9.27"
197
	Name string
198

199
	// The type of the value eg Integer
200
	Type Asn1BER
201
}
202

203
const AsnContext = 0x80
204
const AsnExtensionID = 0x1F
205
const AsnExtensionTag = (AsnContext | AsnExtensionID) // 0x9F
206

207
//go:generate stringer -type Asn1BER
208

209
// Asn1BER is the type of the SNMP PDU
210
type Asn1BER byte
211

212
// Asn1BER's - http://www.ietf.org/rfc/rfc1442.txt
213
const (
214
	EndOfContents     Asn1BER = 0x00
215
	UnknownType       Asn1BER = 0x00
216
	Boolean           Asn1BER = 0x01
217
	Integer           Asn1BER = 0x02
218
	BitString         Asn1BER = 0x03
219
	OctetString       Asn1BER = 0x04
220
	Null              Asn1BER = 0x05
221
	ObjectIdentifier  Asn1BER = 0x06
222
	ObjectDescription Asn1BER = 0x07
223
	IPAddress         Asn1BER = 0x40
224
	Counter32         Asn1BER = 0x41
225
	Gauge32           Asn1BER = 0x42
226
	TimeTicks         Asn1BER = 0x43
227
	Opaque            Asn1BER = 0x44
228
	NsapAddress       Asn1BER = 0x45
229
	Counter64         Asn1BER = 0x46
230
	Uinteger32        Asn1BER = 0x47
231
	OpaqueFloat       Asn1BER = 0x78
232
	OpaqueDouble      Asn1BER = 0x79
233
	NoSuchObject      Asn1BER = 0x80
234
	NoSuchInstance    Asn1BER = 0x81
235
	EndOfMibView      Asn1BER = 0x82
236
)
237

238
//go:generate stringer -type SNMPError
239

240
// SNMPError is the type for standard SNMP errors.
241
type SNMPError uint8
242

243
// SNMP Errors
244
const (
245
	NoError             SNMPError = iota // No error occurred. This code is also used in all request PDUs, since they have no error status to report.
246
	TooBig                               // The size of the Response-PDU would be too large to transport.
247
	NoSuchName                           // The name of a requested object was not found.
248
	BadValue                             // A value in the request didn't match the structure that the recipient of the request had for the object. For example, an object in the request was specified with an incorrect length or type.
249
	ReadOnly                             // An attempt was made to set a variable that has an Access value indicating that it is read-only.
250
	GenErr                               // An error occurred other than one indicated by a more specific error code in this table.
251
	NoAccess                             // Access was denied to the object for security reasons.
252
	WrongType                            // The object type in a variable binding is incorrect for the object.
253
	WrongLength                          // A variable binding specifies a length incorrect for the object.
254
	WrongEncoding                        // A variable binding specifies an encoding incorrect for the object.
255
	WrongValue                           // The value given in a variable binding is not possible for the object.
256
	NoCreation                           // A specified variable does not exist and cannot be created.
257
	InconsistentValue                    // A variable binding specifies a value that could be held by the variable but cannot be assigned to it at this time.
258
	ResourceUnavailable                  // An attempt to set a variable required a resource that is not available.
259
	CommitFailed                         // An attempt to set a particular variable failed.
260
	UndoFailed                           // An attempt to set a particular variable as part of a group of variables failed, and the attempt to then undo the setting of other variables was not successful.
261
	AuthorizationError                   // A problem occurred in authorization.
262
	NotWritable                          // The variable cannot be written or created.
263
	InconsistentName                     // The name in a variable binding specifies a variable that does not exist.
264
)
265

266
//
267
// Public Functions (main interface)
268
//
269

270
// Connect creates and opens a socket. Because UDP is a connectionless
271
// protocol, you won't know if the remote host is responding until you send
272
// packets. Neither will you know if the host is regularly disappearing and reappearing.
273
//
274
// For historical reasons (ie this is part of the public API), the method won't
275
// be renamed to Dial().
276
func (x *GoSNMP) Connect() error {
277
	return x.connect("")
278
}
279

280
// ConnectIPv4 forces an IPv4-only connection
281
func (x *GoSNMP) ConnectIPv4() error {
282
	return x.connect("4")
283
}
284

285
// ConnectIPv6 forces an IPv6-only connection
286
func (x *GoSNMP) ConnectIPv6() error {
287
	return x.connect("6")
288
}
289

290
// connect to address addr on the given network
291
//
292
// https://golang.org/pkg/net/#Dial gives acceptable network values as:
293
//
294
//	"tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "udp", "udp4" (IPv4-only),"udp6" (IPv6-only), "ip",
295
//	"ip4" (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and "unixpacket"
296
func (x *GoSNMP) connect(networkSuffix string) error {
297
	err := x.validateParameters()
298
	if err != nil {
299
		return err
300
	}
301

302
	x.Transport += networkSuffix
303
	if err = x.netConnect(); err != nil {
304
		return fmt.Errorf("error establishing connection to host: %w", err)
305
	}
306

307
	if x.random == 0 {
308
		n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) // returns a uniform random value in [0, 2147483647].
309
		if err != nil {
310
			return fmt.Errorf("error occurred while generating random: %w", err)
311
		}
312
		x.random = uint32(n.Uint64())
313
	}
314
	// http://tools.ietf.org/html/rfc3412#section-6 - msgID only uses the first 31 bits
315
	// msgID INTEGER (0..2147483647)
316
	x.msgID = x.random
317

318
	// RequestID is Integer32 from SNMPV2-SMI and uses all 32 bits
319
	x.requestID = x.random
320

321
	x.rxBuf = new([rxBufSize]byte)
322

323
	return nil
324
}
325

326
// Performs the real socket opening network operation. This can be used to do a
327
// reconnect (needed for TCP)
328
func (x *GoSNMP) netConnect() error {
329
	var err error
330
	var localAddr net.Addr
331
	addr := net.JoinHostPort(x.Target, strconv.Itoa(int(x.Port)))
332

333
	switch x.Transport {
334
	case "udp", "udp4", "udp6":
335
		if localAddr, err = net.ResolveUDPAddr(x.Transport, x.LocalAddr); err != nil {
336
			return err
337
		}
338
		if addr4 := localAddr.(*net.UDPAddr).IP.To4(); addr4 != nil {
339
			x.Transport = "udp4"
340
		}
341
		if x.UseUnconnectedUDPSocket {
342
			x.uaddr, err = net.ResolveUDPAddr(x.Transport, addr)
343
			if err != nil {
344
				return err
345
			}
346
			x.Conn, err = net.ListenUDP(x.Transport, localAddr.(*net.UDPAddr))
347
			return err
348
		}
349
	case "tcp", "tcp4", "tcp6":
350
		if localAddr, err = net.ResolveTCPAddr(x.Transport, x.LocalAddr); err != nil {
351
			return err
352
		}
353
		if addr4 := localAddr.(*net.TCPAddr).IP.To4(); addr4 != nil {
354
			x.Transport = "tcp4"
355
		}
356
	}
357
	dialer := net.Dialer{Timeout: x.Timeout, LocalAddr: localAddr, Control: x.Control}
358
	x.Conn, err = dialer.DialContext(x.Context, x.Transport, addr)
359
	return err
360
}
361

362
func (x *GoSNMP) validateParameters() error {
363
	if x.Transport == "" {
364
		x.Transport = udp
365
	}
366

367
	if x.MaxOids == 0 {
368
		x.MaxOids = MaxOids
369
	} else if x.MaxOids < 0 {
370
		return fmt.Errorf("field MaxOids cannot be less than 0")
371
	}
372

373
	if x.Version == Version3 {
374
		// TODO: setting the Reportable flag violates rfc3412#6.4 if PDU is of type SNMPv2Trap.
375
		// See if we can do this smarter and remove bitclear fix from trap.go:57
376
		x.MsgFlags |= Reportable // tell the snmp server that a report PDU MUST be sent
377

378
		err := x.validateParametersV3()
379
		if err != nil {
380
			return err
381
		}
382
		err = x.SecurityParameters.init(x.Logger)
383
		if err != nil {
384
			return err
385
		}
386
	}
387

388
	if x.Context == nil {
389
		x.Context = context.Background()
390
	}
391
	return nil
392
}
393

394
func (x *GoSNMP) MkSnmpPacket(pdutype PDUType, pdus []SnmpPDU, nonRepeaters uint8, maxRepetitions uint32) *SnmpPacket {
395
	return x.mkSnmpPacket(pdutype, pdus, nonRepeaters, maxRepetitions)
396
}
397

398
func (x *GoSNMP) mkSnmpPacket(pdutype PDUType, pdus []SnmpPDU, nonRepeaters uint8, maxRepetitions uint32) *SnmpPacket {
399
	var newSecParams SnmpV3SecurityParameters
400
	if x.SecurityParameters != nil {
401
		newSecParams = x.SecurityParameters.Copy()
402
	}
403
	return &SnmpPacket{
404
		Version:            x.Version,
405
		Community:          x.Community,
406
		MsgFlags:           x.MsgFlags,
407
		SecurityModel:      x.SecurityModel,
408
		SecurityParameters: newSecParams,
409
		ContextEngineID:    x.ContextEngineID,
410
		ContextName:        x.ContextName,
411
		Error:              0,
412
		ErrorIndex:         0,
413
		PDUType:            pdutype,
414
		NonRepeaters:       nonRepeaters,
415
		MaxRepetitions:     (maxRepetitions & 0x7FFFFFFF),
416
		Variables:          pdus,
417
	}
418
}
419

420
// Get sends an SNMP GET request
421
func (x *GoSNMP) Get(oids []string) (result *SnmpPacket, err error) {
422
	oidCount := len(oids)
423
	if oidCount > x.MaxOids {
424
		return nil, fmt.Errorf("oid count (%d) is greater than MaxOids (%d)",
425
			oidCount, x.MaxOids)
426
	}
427
	// convert oids slice to pdu slice
428
	pdus := make([]SnmpPDU, 0, oidCount)
429
	for _, oid := range oids {
430
		pdus = append(pdus, SnmpPDU{Name: oid, Type: Null, Value: nil})
431
	}
432
	// build up SnmpPacket
433
	packetOut := x.mkSnmpPacket(GetRequest, pdus, 0, 0)
434
	return x.send(packetOut, true)
435
}
436

437
// Set sends an SNMP SET request
438
func (x *GoSNMP) Set(pdus []SnmpPDU) (result *SnmpPacket, err error) {
439
	var packetOut *SnmpPacket
440
	switch pdus[0].Type {
441
	// TODO test Gauge32
442
	case Integer, OctetString, Gauge32, IPAddress, ObjectIdentifier, Counter32, Counter64, Null, TimeTicks, Uinteger32, OpaqueFloat, OpaqueDouble:
443
		packetOut = x.mkSnmpPacket(SetRequest, pdus, 0, 0)
444
	default:
445
		return nil, fmt.Errorf("ERR:gosnmp currently only supports SNMP SETs for Integer, OctetString, Gauge32, IPAddress, ObjectIdentifier, Counter32, Counter64, Null, TimeTicks, Uinteger32, OpaqueFloat, and OpaqueDouble. Not %s", pdus[0].Type)
446
	}
447
	return x.send(packetOut, true)
448
}
449

450
// GetNext sends an SNMP GETNEXT request
451
func (x *GoSNMP) GetNext(oids []string) (result *SnmpPacket, err error) {
452
	oidCount := len(oids)
453
	if oidCount > x.MaxOids {
454
		return nil, fmt.Errorf("oid count (%d) is greater than MaxOids (%d)",
455
			oidCount, x.MaxOids)
456
	}
457

458
	// convert oids slice to pdu slice
459
	pdus := make([]SnmpPDU, 0, oidCount)
460
	for _, oid := range oids {
461
		pdus = append(pdus, SnmpPDU{Name: oid, Type: Null, Value: nil})
462
	}
463

464
	// Marshal and send the packet
465
	packetOut := x.mkSnmpPacket(GetNextRequest, pdus, 0, 0)
466

467
	return x.send(packetOut, true)
468
}
469

470
// GetBulk sends an SNMP GETBULK request
471
//
472
// For maxRepetitions greater than 255, use BulkWalk() or BulkWalkAll()
473
func (x *GoSNMP) GetBulk(oids []string, nonRepeaters uint8, maxRepetitions uint32) (result *SnmpPacket, err error) {
474
	if x.Version == Version1 {
475
		return nil, fmt.Errorf("GETBULK not supported in SNMPv1")
476
	}
477
	oidCount := len(oids)
478
	if oidCount > x.MaxOids {
479
		return nil, fmt.Errorf("oid count (%d) is greater than MaxOids (%d)",
480
			oidCount, x.MaxOids)
481
	}
482

483
	// convert oids slice to pdu slice
484
	pdus := make([]SnmpPDU, 0, oidCount)
485
	for _, oid := range oids {
486
		pdus = append(pdus, SnmpPDU{Name: oid, Type: Null, Value: nil})
487
	}
488

489
	// Marshal and send the packet
490
	packetOut := x.mkSnmpPacket(GetBulkRequest, pdus, nonRepeaters, maxRepetitions)
491
	return x.send(packetOut, true)
492
}
493

494
// SnmpEncodePacket exposes SNMP packet generation to external callers.
495
// This is useful for generating traffic for use over separate transport
496
// stacks and creating traffic samples for test purposes.
497
func (x *GoSNMP) SnmpEncodePacket(pdutype PDUType, pdus []SnmpPDU, nonRepeaters uint8, maxRepetitions uint32) ([]byte, error) {
498
	err := x.validateParameters()
499
	if err != nil {
500
		return []byte{}, err
501
	}
502

503
	pkt := x.mkSnmpPacket(pdutype, pdus, nonRepeaters, maxRepetitions)
504

505
	// Request ID is an atomic counter that wraps to 0 at max int32.
506
	reqID := (atomic.AddUint32(&(x.requestID), 1) & 0x7FFFFFFF)
507

508
	pkt.RequestID = reqID
509

510
	if x.Version == Version3 {
511
		msgID := (atomic.AddUint32(&(x.msgID), 1) & 0x7FFFFFFF)
512

513
		pkt.MsgID = msgID
514

515
		err = x.initPacket(pkt)
516
		if err != nil {
517
			return []byte{}, err
518
		}
519
	}
520

521
	var out []byte
522
	out, err = pkt.marshalMsg()
523
	if err != nil {
524
		return []byte{}, err
525
	}
526

527
	return out, nil
528
}
529

530
// SnmpDecodePacket exposes SNMP packet parsing to external callers.
531
// This is useful for processing traffic from other sources and
532
// building test harnesses.
533
func (x *GoSNMP) SnmpDecodePacket(resp []byte) (*SnmpPacket, error) {
534
	var err error
535

536
	result := &SnmpPacket{}
537

538
	err = x.validateParameters()
539
	if err != nil {
540
		return result, err
541
	}
542

543
	result.Logger = x.Logger
544
	if x.SecurityParameters != nil {
545
		result.SecurityParameters = x.SecurityParameters.Copy()
546
	}
547

548
	var cursor int
549
	cursor, err = x.unmarshalHeader(resp, result)
550
	if err != nil {
551
		err = fmt.Errorf("unable to decode packet header: %w", err)
552
		return result, err
553
	}
554

555
	if result.Version == Version3 {
556
		resp, cursor, err = x.decryptPacket(resp, cursor, result)
557
		if err != nil {
558
			return result, err
559
		}
560
	}
561

562
	err = x.unmarshalPayload(resp, cursor, result)
563
	if err != nil {
564
		err = fmt.Errorf("unable to decode packet body: %w", err)
565
		return result, err
566
	}
567

568
	return result, nil
569
}
570

571
// SetRequestID sets the base ID value for future requests
572
func (x *GoSNMP) SetRequestID(reqID uint32) {
573
	x.requestID = reqID & 0x7fffffff
574
}
575

576
// SetMsgID sets the base ID value for future messages
577
func (x *GoSNMP) SetMsgID(msgID uint32) {
578
	x.msgID = msgID & 0x7fffffff
579
}
580

581
//
582
// SNMP Walk functions - Analogous to net-snmp's snmpwalk commands
583
//
584

585
// WalkFunc is the type of the function called for each data unit visited
586
// by the Walk function.  If an error is returned processing stops.
587
type WalkFunc func(dataUnit SnmpPDU) error
588

589
// BulkWalk retrieves a subtree of values using GETBULK. As the tree is
590
// walked walkFn is called for each new value. The function immediately returns
591
// an error if either there is an underlaying SNMP error (e.g. GetBulk fails),
592
// or if walkFn returns an error.
593
func (x *GoSNMP) BulkWalk(rootOid string, walkFn WalkFunc) error {
594
	return x.walk(GetBulkRequest, rootOid, walkFn)
595
}
596

597
// BulkWalkAll is similar to BulkWalk but returns a filled array of all values
598
// rather than using a callback function to stream results. Caution: if you
599
// have set x.AppOpts to 'c', BulkWalkAll may loop indefinitely and cause an
600
// Out Of Memory - use BulkWalk instead.
601
func (x *GoSNMP) BulkWalkAll(rootOid string) (results []SnmpPDU, err error) {
602
	return x.walkAll(GetBulkRequest, rootOid)
603
}
604

605
// Walk retrieves a subtree of values using GETNEXT - a request is made for each
606
// value, unlike BulkWalk which does this operation in batches. As the tree is
607
// walked walkFn is called for each new value. The function immediately returns
608
// an error if either there is an underlaying SNMP error (e.g. GetNext fails),
609
// or if walkFn returns an error.
610
func (x *GoSNMP) Walk(rootOid string, walkFn WalkFunc) error {
611
	return x.walk(GetNextRequest, rootOid, walkFn)
612
}
613

614
// WalkAll is similar to Walk but returns a filled array of all values rather
615
// than using a callback function to stream results. Caution: if you have set
616
// x.AppOpts to 'c', WalkAll may loop indefinitely and cause an Out Of Memory -
617
// use Walk instead.
618
func (x *GoSNMP) WalkAll(rootOid string) (results []SnmpPDU, err error) {
619
	return x.walkAll(GetNextRequest, rootOid)
620
}
621

622
//
623
// Public Functions (helpers) - in alphabetical order
624
//
625

626
// Partition - returns true when dividing a slice into
627
// partitionSize lengths, including last partition which may be smaller
628
// than partitionSize. This is useful when you have a large array of OIDs
629
// to run Get() on. See the tests for example usage.
630
//
631
// For example for a slice of 8 items to be broken into partitions of
632
// length 3, Partition returns true for the currentPosition having
633
// the following values:
634
//
635
// 0  1  2  3  4  5  6  7
636
//
637
//	T        T     T
638
func Partition(currentPosition, partitionSize, sliceLength int) bool {
639
	if currentPosition < 0 || currentPosition >= sliceLength {
640
		return false
641
	}
642
	if partitionSize == 1 { // redundant, but an obvious optimisation
643
		return true
644
	}
645
	if currentPosition%partitionSize == partitionSize-1 {
646
		return true
647
	}
648
	if currentPosition == sliceLength-1 {
649
		return true
650
	}
651
	return false
652
}
653

654
// ToBigInt converts SnmpPDU.Value to big.Int, or returns a zero big.Int for
655
// non int-like types (eg strings).
656
//
657
// This is a convenience function to make working with SnmpPDU's easier - it
658
// reduces the need for type assertions. A big.Int is convenient, as SNMP can
659
// return int32, uint32, and uint64.
660
func ToBigInt(value interface{}) *big.Int {
661
	var val int64
662

663
	switch value := value.(type) { // shadow
664
	case int:
665
		val = int64(value)
666
	case int8:
667
		val = int64(value)
668
	case int16:
669
		val = int64(value)
670
	case int32:
671
		val = int64(value)
672
	case int64:
673
		val = value
674
	case uint:
675
		val = int64(value)
676
	case uint8:
677
		val = int64(value)
678
	case uint16:
679
		val = int64(value)
680
	case uint32:
681
		val = int64(value)
682
	case uint64: // beware: int64(MaxUint64) overflow, handle different
683
		return new(big.Int).SetUint64(value)
684
	case string:
685
		// for testing and other apps - numbers may appear as strings
686
		var err error
687
		if val, err = strconv.ParseInt(value, 10, 64); err != nil {
688
			val = 0
689
		}
690
	default:
691
		val = 0
692
	}
693

694
	return big.NewInt(val)
695
}
696

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.