Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
all:gowalk goget trapd
trapd: utils/trapd.go snmp.go ber.go
go build utils/trapd.go
gowalk: utils/gowalk.go snmp.go ber.go
go build utils/gowalk.go
goget: utils/goget.go snmp.go ber.go
go build utils/goget.go

#Windows Binary
win_trapd: utils/trapd.go snmp.go ber.go
GOOS=windows GOARCH=amd64 go build utils/trapd.go
win_gowalk: utils/gowalk.go snmp.go ber.go
GOOS=windows GOARCH=amd64 go build utils/gowalk.go
win_goget: utils/goget.go snmp.go ber.go
GOOS=windows GOARCH=amd64 go build utils/goget.go

clean:
rm -f trapd gowalk goget *.exe

47 changes: 20 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
WapSnmp : SNMP client for golang
--------------------------------
Currently supported operations:
* SNMP v1/v2c/v3 trap receiver with V3 EngineID auto discovery
* SNMP v1/v2c Get, GetMultiple, GetNext, GetBulk, Walk
* SNMP V3 Get, Walk, GetNext

This is an open-source SNMP client library for Go. This allows you to query SNMP servers for any variable, given it's OID (no MIB resolution).

This library has been written to be in Go style and that means it should be very resistent to all conditions. It's entirely non-blocking/asynchronous and very, very fast. It's also surprisingly small and easy to understand.
Compile
--------------------------------
make

It supports SNMPv2c or lower (not 3, due to it's complexity), and supports all methods provided as part of that standard. Get, GetMultiple (which are really the same request, but ...), GetNext and GetBulk.
This will compile the following binaries:
* goget : get single SNMP mib using SNMP v3
* gowalk : walk SNMP mibs using SNMP v3
* trapd : this program is able to receive SNMP v2 and v3 traps (you need to configure users for SNMP v3 traps)

It has been tested on juniper and cisco devices and has proven to remain stable over long periods of time.
You can run "go test" to perform unit test.

Example:
Using the code
---------------------------------
* The *_test.go files provide good examples of how to use these functions
* file uder utils/ contain the main entry to the utility program. Then look at example.go and snmp.go to see how it works.

func DoGetTableTest(target string) {
community := "public"
version := SNMPv2c
Not supported yet:
------------------
* SNMP Informs receiver
* SNMP v3 GetMultiple, GetBulk (these can be easily implemented since SNMP v3 Walk/Get/GetNext is working)

oid := MustParseOid(".1.3.6.1.4.1.2636.3.2.3.1.20")

fmt.Printf("Contacting %v %v %v\n", target, community, version)
wsnmp, err := NewWapSNMP(target, community, version, 2*time.Second, 5)
defer wsnmp.Close()
if err != nil {
fmt.Printf("Error creating wsnmp => %v\n", wsnmp)
return
}

table, err := wsnmp.GetTable(oid)
if err != nil {
fmt.Printf("Error getting table => %v\n", wsnmp)
return
}
for k, v := range table {
fmt.Printf("%v => %v\n", k, v)
}
}

This library can also be used as a ASN1 BER parser.
66 changes: 54 additions & 12 deletions ber.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ const (
AsnGetRequest BERType = 0xa0
AsnGetNextRequest BERType = 0xa1
AsnGetResponse BERType = 0xa2
AsnSetRequest BERType = 0xa3
AsnTrap BERType = 0xa4
AsnGetBulkRequest BERType = 0xa5
AsnInform BERType = 0xa6
AsnTrap2 BERType = 0xa7
AsnReport BERType = 0xa8
)

// Type to indicate which SNMP version is in use.
Expand All @@ -79,6 +84,7 @@ type SNMPVersion uint8
const (
SNMPv1 SNMPVersion = 0
SNMPv2c SNMPVersion = 1
SNMPv3 SNMPVersion = 3
)

// EncodeLength encodes an integer value as a BER compliant length value.
Expand Down Expand Up @@ -139,6 +145,18 @@ func DecodeLength(toparse []byte) (int, int, error) {
return val, 1 + numOctets, nil
}

func DecodeCounter64(toparse []byte) (uint64, error) {
if len(toparse) > 8 {
return 0, fmt.Errorf("don't support more than 64 bits")
}
var val uint64;
val = 0
for _, b := range toparse {
val = val*256 + uint64(b)
}
return val, nil
}

/* DecodeInteger decodes an integer.

Will error out if it's longer than 64 bits. */
Expand All @@ -153,22 +171,32 @@ func DecodeInteger(toparse []byte) (int, error) {
return val, nil
}

func DecodeIPAddress(toparse []byte) (string, error) {
if len(toparse) != 4 {
return "", fmt.Errorf("need 4 bytes for IP address")
}
return fmt.Sprintf("%d.%d.%d.%d",toparse[0],toparse[1],toparse[2],toparse[3]),nil
}


// EncodeInteger encodes an integer to BER format.
func EncodeInteger(toEncode int) []byte {
// Calculate the length we'll need for the encoded value.
l := 1
if toEncode==0 {
return []byte{0};
}
result := make([]byte, 8)
pos := 7
i := toEncode
for i > 255 {
for i > 0{
result[pos]=byte(i%256);
i = i >> 8
l++
pos--;
}

// Now create a byte array of the correct length and copy the value into it.
result := make([]byte, l)
for i = 0; i < l; i++ {
result[i] = byte(toEncode >> uint(8*(l-i-1)))
if (result[pos+1]>=0x80){
result[pos]=0x00;
pos--;
}
return result
return result[pos+1:8];
}

// DecodeSequence decodes BER binary data into into *[]interface{}.
Expand All @@ -195,6 +223,7 @@ func DecodeSequence(toparse []byte) ([]interface{}, error) {

lidx := 0
idx := 1 + seqLenLen
toparse=toparse[:(1+seqLenLen+seqLength)]
// Let's guarantee progress.
for idx < len(toparse) && idx > lidx {
berType := toparse[idx]
Expand Down Expand Up @@ -227,25 +256,38 @@ func DecodeSequence(toparse []byte) ([]interface{}, error) {
return nil, err
}
result = append(result, *oid)
case Gauge32:
case Gauge32,Counter32:
val, err := DecodeInteger(berValue)
if err != nil {
return nil, err
}
result = append(result, val)
case Counter64:
val, err := DecodeCounter64(berValue)
if err != nil {
return nil, err
}
result = append(result, val)

case Timeticks:
val, err := DecodeInteger(berValue)
if err != nil {
return nil, err
}
result = append(result, time.Duration(val)*10*time.Millisecond)
case Ipaddress:
val, err := DecodeIPAddress(berValue)
if err != nil {
return nil, err
}
result = append(result, val)
case Sequence:
pdu, err := DecodeSequence(berAll)
if err != nil {
return nil, err
}
result = append(result, pdu)
case AsnGetNextRequest, AsnGetRequest, AsnGetResponse:
case AsnGetNextRequest, AsnGetRequest, AsnGetResponse, AsnReport, AsnTrap2, AsnTrap:
pdu, err := DecodeSequence(berAll)
if err != nil {
return nil, err
Expand Down
10 changes: 5 additions & 5 deletions ber_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ type LengthTest struct {
func TestLengthDecodingEncoding(t *testing.T) {
tests := []LengthTest{
LengthTest{[]byte{0x26}, 38, 1},
LengthTest{[]byte{0x81, 0xc9}, 201, 2},
LengthTest{[]byte{0x81, 0xca}, 202, 2},
LengthTest{[]byte{0x81, 0x9f}, 159, 2},
LengthTest{[]byte{0x82, 0x01, 0x70}, 368, 3},
LengthTest{[]byte{0x81, 0xe3}, 227, 2},
LengthTest{[]byte{0x82,0x00, 0xc9}, 201, 3},
LengthTest{[]byte{0x82,0x00, 0xca}, 202, 3},
LengthTest{[]byte{0x82,0x00, 0x9f}, 159, 3},
LengthTest{[]byte{0x82,0x01, 0x70}, 368, 3},
LengthTest{[]byte{0x82,0x00, 0xe3}, 227, 3},
}

for _, test := range tests {
Expand Down
69 changes: 54 additions & 15 deletions example.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,57 @@ func DoGetTableTest(target string) {
}

func DoWalkTest(target string) {
community := "public"
community := "monitorhcm"
version := SNMPv2c

oid := MustParseOid(".1.3.6.1.2.1")
oid := MustParseOid(".1.3.6.1.2.1.1")
oid0 := oid;

fmt.Printf("Contacting %v %v %v\n", target, community, version)
wsnmp, err := NewWapSNMP(target, community, version, 2*time.Second, 5)
defer wsnmp.Close()
if err != nil {
fmt.Printf("Error creating wsnmp => %v\n", wsnmp)
return
}
defer wsnmp.Close()
for {
results, err := wsnmp.GetBulk(oid, 50)
if err != nil {
fmt.Printf("GetBulk error => %v\n", err)
return
}
for o, v := range results {
fmt.Printf("%v => %v\n", o, v)

oid = MustParseOid(o)
}
/* Old version without GETBULK
result_oid, val, err := wsnmp.GetNext(oid)
if err != nil {
fmt.Printf("GetNext error => %v\n", err)
return
}
fmt.Printf("GetNext(%v, %v, %v, %v) => %s, %v\n", target, community, version, oid, result_oid, val)
oid = *result_oid
*/
if ! oid.Within(oid0) {
break;
}
}
}

func DoWalkTestV3(target string, oidstr,username, authAlg, authKey, privAlg, privKey string) {
oid := MustParseOid(oidstr)
oid0 := oid;

fmt.Printf("Contacting %v using SNMP v3\n", target)
wsnmp, err := NewWapSNMPv3(target, username, authAlg, authKey, privAlg, privKey, 2*time.Second, 2)
if err != nil {
fmt.Printf("Error creating wsnmp => %v\n", wsnmp)
return
}
defer wsnmp.Close()
wsnmp.Discover();
for {
result_oid, val, err := wsnmp.GetNextV3(oid)
if err != nil {
fmt.Printf("GetNext error => %v\n", err)
return
}
fmt.Printf("GetNext(%v, %v) => %s, %v\n", target, oid, result_oid, val)

oid = *result_oid
if ! oid.Within(oid0) {
break;
}
}
}

Expand Down Expand Up @@ -93,3 +111,24 @@ func DoGetTest(target string) {
fmt.Printf("Get(%v, %v, %v, %v) => %v\n", target, community, version, oid, val)
}
}

func DoGetTestV3(target string, oidstr,username, authAlg, authKey, privAlg, privKey string) {
oid := MustParseOid(oidstr)

fmt.Printf("Contacting %v using SNMP v3\n", target)
wsnmp, err := NewWapSNMPv3(target, username, authAlg, authKey, privAlg, privKey, 2*time.Second, 2)
if err != nil {
fmt.Printf("Error creating wsnmp => %v\n", wsnmp)
return
}
defer wsnmp.Close()
wsnmp.Discover();

val, err := wsnmp.GetV3(oid)
if err != nil {
fmt.Printf("GetV3 error => %v\n", err)
return
}
fmt.Printf("GetV3(%v) => %v\n", oid , val)
}

Loading