From 1dd347ab8d79d2f8d19963ba460f21c7040cf4a1 Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Thu, 7 Nov 2013 10:07:52 -0800 Subject: [PATCH 01/21] V3 GetNext working --- ber.go | 32 +++-- example.go | 54 +++++--- snmp.go | 333 +++++++++++++++++++++++++++++++++++++++++++++- utils/getTable.go | 9 +- 4 files changed, 394 insertions(+), 34 deletions(-) diff --git a/ber.go b/ber.go index af96d67..27cc965 100644 --- a/ber.go +++ b/ber.go @@ -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. @@ -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. @@ -155,20 +161,22 @@ func DecodeInteger(toparse []byte) (int, error) { // 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{}. @@ -227,7 +235,7 @@ 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 @@ -245,7 +253,7 @@ func DecodeSequence(toparse []byte) ([]interface{}, error) { return nil, err } result = append(result, pdu) - case AsnGetNextRequest, AsnGetRequest, AsnGetResponse: + case AsnGetNextRequest, AsnGetRequest, AsnGetResponse, AsnReport: pdu, err := DecodeSequence(berAll) if err != nil { return nil, err diff --git a/example.go b/example.go index a07b00e..15af5a7 100644 --- a/example.go +++ b/example.go @@ -30,31 +30,20 @@ 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) @@ -62,7 +51,40 @@ func DoWalkTest(target string) { } 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) { + oid := MustParseOid(".1.3.6.1.2.1.1") + oid0 := oid; + + fmt.Printf("Contacting %v using SNMP v3\n", target) + wsnmp, err := NewWapSNMPv3(target, "tzhang","1234567890","1234567890", 2*time.Second, 2) + if err != nil { + fmt.Printf("Error creating wsnmp => %v\n", wsnmp) + return + } + defer wsnmp.Close() + wsnmp.Discover(); + fmt.Printf("key=% x\n",wsnmp.privKey); + fmt.Printf("ver=%d\n",wsnmp.Version); + + 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) + break; + + oid = *result_oid + if ! oid.Within(oid0) { + break; + } } } @@ -93,3 +115,5 @@ func DoGetTest(target string) { fmt.Printf("Get(%v, %v, %v, %v) => %v\n", target, community, version, oid, val) } } + + diff --git a/snmp.go b/snmp.go index 2fd296a..096b795 100644 --- a/snmp.go +++ b/snmp.go @@ -6,8 +6,16 @@ import ( "log" "math/rand" "net" - "reflect" "time" + "io" + "crypto/md5" + "crypto/sha1" + "crypto/aes" + "crypto/cipher" + "reflect" + "strings" + "bytes" + "encoding/binary" ) // The object type that lets you do SNMP requests. @@ -18,12 +26,57 @@ type WapSNMP struct { timeout time.Duration // Timeout to use for all SNMP packets. retries int // Number of times to retry an operation. conn net.Conn // Cache the UDP connection in the object. + //SNMP V3 variables + user string + authPwd string + privPwd string + engineID string + //V3 temp variables + authKey string + privKey string + engineBoots int32 + engineTime int32 + desIV uint32 + aesIV int64 } const ( bufSize int = 16384 + maxMsgSize int = 65000 ) + +func password_to_key( password string, engineID string, hash_alg string) string{ + h := sha1.New() + if hash_alg=="MD5" { + h = md5.New() + } + + count := 0; + plen:=len(password); + repeat := 1048576/plen; + remain := 1048576%plen; + for count < repeat { + io.WriteString(h,password); + count++; + } + if remain > 0 { + io.WriteString(h,string(password[:remain])); + } + ku := string(h.Sum(nil)) + //fmt.Printf("ku=% x\n", ku) + + h.Reset(); + io.WriteString(h,ku); + io.WriteString(h,engineID); + io.WriteString(h,ku); + localKey:=h.Sum(nil); + //fmt.Printf("localKey=% x\n", localKey) + + return string(localKey); +} + + // NewWapSNMP creates a new WapSNMP object. Opens a udp connection to the device that will be used for the SNMP packets. func NewWapSNMP(target, community string, version SNMPVersion, timeout time.Duration, retries int) (*WapSNMP, error) { targetPort := fmt.Sprintf("%s:161", target) @@ -31,14 +84,47 @@ func NewWapSNMP(target, community string, version SNMPVersion, timeout time.Dura if err != nil { return nil, fmt.Errorf(`error connecting to ("udp", "%s") : %s`, targetPort, err) } - return &WapSNMP{target, community, version, timeout, retries, conn}, nil + return &WapSNMP{ + Target:target, + Community: community, + Version: version, + timeout: timeout, + retries: retries, + conn: conn, + }, nil +} + +func NewWapSNMPv3(target, user, authPwd, privPwd string, timeout time.Duration, retries int) (*WapSNMP, error) { + targetPort := fmt.Sprintf("%s:161", target) + conn, err := net.DialTimeout("udp", targetPort, timeout) + if err != nil { + return nil, fmt.Errorf(`error connecting to ("udp", "%s") : %s`, targetPort, err) + } + return &WapSNMP{ + Target:target, + Version: SNMPv3, + timeout: timeout, + retries: retries, + conn: conn, + user: user, + authPwd: authPwd, + privPwd: privPwd, + }, nil + } /* NewWapSNMPOnConn creates a new WapSNMP object from an existing net.Conn. It does not check if the provided target is valid.*/ func NewWapSNMPOnConn(target, community string, version SNMPVersion, timeout time.Duration, retries int, conn net.Conn) *WapSNMP { - return &WapSNMP{target, community, version, timeout, retries, conn} + return &WapSNMP{ + Target:target, + Community: community, + Version: version, + timeout: timeout, + retries: retries, + conn: conn, + } } // Generate a valid SNMP request ID. @@ -148,6 +234,247 @@ func (w WapSNMP) GetMultiple(oids []Oid) (map[string]interface{}, error) { return result, nil } +func (w *WapSNMP) Discover() (error) { + msgID := getRandomRequestID() + requestID := getRandomRequestID() + v3Header, _:= EncodeSequence([]interface{}{Sequence,"",0,0,"","",""}) + flags:=string([]byte{4}); + USM := 0x03; + req, err := EncodeSequence([]interface{}{ + Sequence, int(w.Version), + []interface{}{Sequence, msgID, maxMsgSize, flags, USM}, + string(v3Header), + []interface{}{Sequence, "", "", + []interface{}{AsnGetRequest, requestID, 0, 0, []interface{}{Sequence} }}}) + if err != nil { + return err + } + + response := make([]byte, bufSize) + numRead, err := poll(w.conn, req, response, w.retries, 500*time.Millisecond) + if err != nil { + return err + } + fmt.Printf("numRead=%d\n",numRead); + + decodedResponse, err := DecodeSequence(response[:numRead]) + if err != nil { + fmt.Printf("Error decoding discover:%v\n",err); + panic(err); + } + + v3HeaderStr := decodedResponse[3].(string); + if false { + for i, val := range decodedResponse{ + fmt.Printf("%v:type=%v\n",i,reflect.TypeOf(val)); + } + } + + v3HeaderDecoded, err := DecodeSequence([]byte(v3HeaderStr)) + if err != nil { + fmt.Printf("Error 2 decoding:%v\n",err); + return err + } + + if false { + for i, val := range v3HeaderDecoded{ + fmt.Printf("%v:type=%v\n",i,reflect.TypeOf(val)) + } + } + + w.engineID=v3HeaderDecoded[1].(string); + w.engineBoots=int32(v3HeaderDecoded[2].(int)); + w.engineTime=int32(v3HeaderDecoded[3].(int)); + w.aesIV=rand.Int63(); + //keys + w.authKey = password_to_key(w.authPwd, w.engineID , "SHA1"); + privKey := password_to_key(w.privPwd, w.engineID , "SHA1"); + w.privKey = string(([]byte(privKey))[0:16]) + return nil +} + +func EncryptAESCFB(dst, src, key, iv []byte) error { + aesBlockEncrypter, err := aes.NewCipher([]byte(key)) + if err != nil { + return err + } + aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncrypter, iv) + aesEncrypter.XORKeyStream(dst, src) + return nil +} + +func DecryptAESCFB(dst, src, key, iv []byte) error { + aesBlockDecrypter, err := aes.NewCipher([]byte(key)) + if err != nil { + return nil + } + aesDecrypter := cipher.NewCFBDecrypter(aesBlockDecrypter, iv) + aesDecrypter.XORKeyStream(dst, src) + return nil +} + + +func strXor(s1,s2 string) (string) { + if len(s1) != len(s2){ + panic("strXor called with two strings of different length\n"); + } + n := len(s1); + b := make([]byte, n); + for i:=0; i %v\n", err) return From bb85250012f5e3ee0a41e6457fac3c722c1d1e81 Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Thu, 7 Nov 2013 10:33:50 -0800 Subject: [PATCH 02/21] removed debug messages --- example.go | 4 ---- snmp.go | 16 ++-------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/example.go b/example.go index 15af5a7..3c1b750 100644 --- a/example.go +++ b/example.go @@ -69,9 +69,6 @@ func DoWalkTestV3(target string) { } defer wsnmp.Close() wsnmp.Discover(); - fmt.Printf("key=% x\n",wsnmp.privKey); - fmt.Printf("ver=%d\n",wsnmp.Version); - for { result_oid, val, err := wsnmp.GetNextV3(oid) if err != nil { @@ -79,7 +76,6 @@ func DoWalkTestV3(target string) { return } fmt.Printf("GetNext(%v, %v) => %s, %v\n", target, oid, result_oid, val) - break; oid = *result_oid if ! oid.Within(oid0) { diff --git a/snmp.go b/snmp.go index 096b795..69bea40 100644 --- a/snmp.go +++ b/snmp.go @@ -340,7 +340,6 @@ func (w WapSNMP) auth(wholeMsg string ) (string) { h.Reset(); io.WriteString(h,k2 + tmp1); msgAuthParam := string(h.Sum(nil)[:12]); - fmt.Printf("% x\n",msgAuthParam); return msgAuthParam; } @@ -354,7 +353,6 @@ func (w WapSNMP) encrypt(payload string ) (string,string) { binary.Write(buf3, binary.BigEndian, w.aesIV) privParam := string(buf3.Bytes()) iv := string(buf.Bytes()) + string(buf2.Bytes()) + privParam - fmt.Printf("encrypt iv=% x, privParam=% x\n key=% x\n",iv,privParam,w.privKey); // Encrypt encrypted := make([]byte, len(payload)) @@ -383,7 +381,6 @@ func (w WapSNMP) decrypt(payload,privParam string ) string { // GetNext issues a GETNEXT SNMP request. func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { - fmt.Printf("key=% x\n",w.privKey); msgID := getRandomRequestID() requestID := getRandomRequestID() req, err := EncodeSequence( @@ -394,14 +391,11 @@ func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { if err != nil { panic(err); } - fmt.Printf("req=% x\n",req); encrypted,privParam := w.encrypt(string(req)); - fmt.Printf("ereq=% x\n",encrypted); v3Header, err:= EncodeSequence([]interface{}{Sequence,w.engineID, int(w.engineBoots),int(w.engineTime),w.user,strings.Repeat("\x00",12),privParam}) - fmt.Printf("v3header=% x\n",v3Header); if err != nil { panic(err); } @@ -424,16 +418,17 @@ func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { if err != nil { return nil, nil, err } - fmt.Printf("numRead=%d, % x\n",numRead,response[:numRead]); decodedResponse, err := DecodeSequence(response[:numRead]) if err != nil { fmt.Printf("Error decoding getNext:%v\n",err); return nil, nil, err } + /* for i, val := range decodedResponse{ fmt.Printf("Resp:%v:type=%v\n",i,reflect.TypeOf(val)); } + */ v3HeaderStr := decodedResponse[3].(string); v3HeaderDecoded, err := DecodeSequence([]byte(v3HeaderStr)) @@ -441,10 +436,6 @@ func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { fmt.Printf("Error 2 decoding:%v\n",err); return nil, nil, err } - for i, val := range v3HeaderDecoded{ - fmt.Printf("v3Header: %v:type=%v\n",i,reflect.TypeOf(val)); - } - w.engineID=v3HeaderDecoded[1].(string) w.engineBoots=int32(v3HeaderDecoded[2].(int)) @@ -460,9 +451,6 @@ func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { fmt.Printf("Error 3 decoding:%v\n",err); return nil, nil, err } - for i, val := range pduDecoded{ - fmt.Printf("%v:type=%v\n",i,reflect.TypeOf(val)); - } // Find the varbinds respPacket := pduDecoded[3].([]interface{}) From a6e37268acd641b0b0d660866e77f323086388f1 Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Thu, 7 Nov 2013 10:42:33 -0800 Subject: [PATCH 03/21] updated README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bf36dce..d0fc305 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ WapSnmp : SNMP client for golang -------------------------------- +# This fork seeks to add SNMP V3 capability +# Original README 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. From 28e542222a84ca89bd09c340a49e29dfe2b1e5ca Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Thu, 7 Nov 2013 13:37:07 -0800 Subject: [PATCH 04/21] aes/des md5/sha1 all working now --- ber.go | 1 + example.go | 4 +- snmp.go | 159 ++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 120 insertions(+), 44 deletions(-) diff --git a/ber.go b/ber.go index 27cc965..5baaefb 100644 --- a/ber.go +++ b/ber.go @@ -203,6 +203,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] diff --git a/example.go b/example.go index 3c1b750..5e05bf5 100644 --- a/example.go +++ b/example.go @@ -62,7 +62,9 @@ func DoWalkTestV3(target string) { oid0 := oid; fmt.Printf("Contacting %v using SNMP v3\n", target) - wsnmp, err := NewWapSNMPv3(target, "tzhang","1234567890","1234567890", 2*time.Second, 2) + //wsnmp, err := NewWapSNMPv3(target, "tzhang",SNMP_SHA1,"1234567890",SNMP_AES,"1234567890", 2*time.Second, 2) + //wsnmp, err := NewWapSNMPv3(target, "tzhang_ma",SNMP_MD5,"1234567890",SNMP_AES,"1234567890", 2*time.Second, 2) + wsnmp, err := NewWapSNMPv3(target, "tzhang_md",SNMP_MD5,"1234567890",SNMP_DES,"1234567890", 2*time.Second, 2) if err != nil { fmt.Printf("Error creating wsnmp => %v\n", wsnmp) return diff --git a/snmp.go b/snmp.go index 69bea40..4cfda2f 100644 --- a/snmp.go +++ b/snmp.go @@ -11,6 +11,7 @@ import ( "crypto/md5" "crypto/sha1" "crypto/aes" + "crypto/des" "crypto/cipher" "reflect" "strings" @@ -28,7 +29,9 @@ type WapSNMP struct { conn net.Conn // Cache the UDP connection in the object. //SNMP V3 variables user string + authAlg string //MD5 or SHA1 authPwd string + privAlg string //AES or DES privPwd string engineID string //V3 temp variables @@ -42,7 +45,11 @@ type WapSNMP struct { const ( bufSize int = 16384 - maxMsgSize int = 65000 + maxMsgSize int = 65500 + SNMP_AES string = "AES" + SNMP_DES string = "DES" + SNMP_SHA1 string = "SHA1" + SNMP_MD5 string = "MD5" ) @@ -94,7 +101,14 @@ func NewWapSNMP(target, community string, version SNMPVersion, timeout time.Dura }, nil } -func NewWapSNMPv3(target, user, authPwd, privPwd string, timeout time.Duration, retries int) (*WapSNMP, error) { +func NewWapSNMPv3(target, user, authAlg, authPwd, privAlg, privPwd string, timeout time.Duration, retries int) (*WapSNMP, error) { + if authAlg != SNMP_MD5 && authAlg != SNMP_SHA1 { + return nil, fmt.Errorf(`Invalid auth algorithm %s, needs SHA1 or MD5`, authAlg) + } + if privAlg != SNMP_AES && privAlg != SNMP_DES { + return nil, fmt.Errorf(`Invalid priv algorithm %s, needs AES or DES`, privAlg) + } + targetPort := fmt.Sprintf("%s:161", target) conn, err := net.DialTimeout("udp", targetPort, timeout) if err != nil { @@ -107,10 +121,11 @@ func NewWapSNMPv3(target, user, authPwd, privPwd string, timeout time.Duration, retries: retries, conn: conn, user: user, + authAlg: authAlg, authPwd: authPwd, + privAlg: privAlg, privPwd: privPwd, }, nil - } /* NewWapSNMPOnConn creates a new WapSNMP object from an existing net.Conn. @@ -234,6 +249,9 @@ func (w WapSNMP) GetMultiple(oids []Oid) (map[string]interface{}, error) { return result, nil } +/* SNMP V3 requires a discover packet being sent before a request being sent, + so that agent's engineID and other parameters can be automatically detected +*/ func (w *WapSNMP) Discover() (error) { msgID := getRandomRequestID() requestID := getRandomRequestID() @@ -247,7 +265,8 @@ func (w *WapSNMP) Discover() (error) { []interface{}{Sequence, "", "", []interface{}{AsnGetRequest, requestID, 0, 0, []interface{}{Sequence} }}}) if err != nil { - return err + fmt.Printf("Error encoding in discover:%v\n",err); + panic(err); } response := make([]byte, bufSize) @@ -255,7 +274,6 @@ func (w *WapSNMP) Discover() (error) { if err != nil { return err } - fmt.Printf("numRead=%d\n",numRead); decodedResponse, err := DecodeSequence(response[:numRead]) if err != nil { @@ -264,35 +282,45 @@ func (w *WapSNMP) Discover() (error) { } v3HeaderStr := decodedResponse[3].(string); - if false { - for i, val := range decodedResponse{ - fmt.Printf("%v:type=%v\n",i,reflect.TypeOf(val)); - } - } - v3HeaderDecoded, err := DecodeSequence([]byte(v3HeaderStr)) if err != nil { fmt.Printf("Error 2 decoding:%v\n",err); return err } - if false { - for i, val := range v3HeaderDecoded{ - fmt.Printf("%v:type=%v\n",i,reflect.TypeOf(val)) - } - } - w.engineID=v3HeaderDecoded[1].(string); w.engineBoots=int32(v3HeaderDecoded[2].(int)); w.engineTime=int32(v3HeaderDecoded[3].(int)); w.aesIV=rand.Int63(); + w.desIV=rand.Uint32(); //keys - w.authKey = password_to_key(w.authPwd, w.engineID , "SHA1"); - privKey := password_to_key(w.privPwd, w.engineID , "SHA1"); + w.authKey = password_to_key(w.authPwd, w.engineID , w.authAlg); + privKey := password_to_key(w.privPwd, w.engineID , w.authAlg); w.privKey = string(([]byte(privKey))[0:16]) return nil } +func EncryptDESCBC(dst, src, key, iv []byte) error { + desBlockEncrypter, err := des.NewCipher([]byte(key)) + if err != nil { + return err + } + desEncrypter := cipher.NewCBCEncrypter(desBlockEncrypter, iv) + desEncrypter.CryptBlocks(dst, src) + return nil +} + +func DecryptDESCBC(dst, src, key, iv []byte) error { + desBlockEncrypter, err := des.NewCipher([]byte(key)) + if err != nil { + return err + } + desDecrypter := cipher.NewCBCDecrypter(desBlockEncrypter, iv) + desDecrypter.CryptBlocks(dst, src) + return nil +} + + func EncryptAESCFB(dst, src, key, iv []byte) error { aesBlockEncrypter, err := aes.NewCipher([]byte(key)) if err != nil { @@ -335,6 +363,9 @@ func (w WapSNMP) auth(wholeMsg string ) (string) { k1 := strXor(eAuthKey,ipad); k2 := strXor(eAuthKey,opad); h := sha1.New() + if w.authAlg == "MD5" { + h = md5.New() + } io.WriteString(h,k1 + wholeMsg); tmp1 := string(h.Sum(nil)); h.Reset(); @@ -346,37 +377,74 @@ func (w WapSNMP) auth(wholeMsg string ) (string) { func (w WapSNMP) encrypt(payload string ) (string,string) { buf := new(bytes.Buffer); binary.Write(buf, binary.BigEndian, w.engineBoots) - buf2 := new(bytes.Buffer); - binary.Write(buf2, binary.BigEndian, w.engineTime) - buf3 := new(bytes.Buffer); - w.aesIV+=1; - binary.Write(buf3, binary.BigEndian, w.aesIV) - privParam := string(buf3.Bytes()) - iv := string(buf.Bytes()) + string(buf2.Bytes()) + privParam - - // Encrypt - encrypted := make([]byte, len(payload)) - err := EncryptAESCFB(encrypted, []byte(payload), []byte(w.privKey), []byte(iv)) - if err != nil { - panic(err) + if w.privAlg == SNMP_AES { + buf2 := new(bytes.Buffer); + binary.Write(buf2, binary.BigEndian, w.engineTime) + buf3 := new(bytes.Buffer); + w.aesIV+=1; + binary.Write(buf3, binary.BigEndian, w.aesIV) + privParam := string(buf3.Bytes()) + iv := string(buf.Bytes()) + string(buf2.Bytes()) + privParam + + // AES Encrypt + encrypted := make([]byte, len(payload)) + err := EncryptAESCFB(encrypted, []byte(payload), []byte(w.privKey), []byte(iv)) + if err != nil { + panic(err) + } + return string(encrypted),privParam; + }else{ + desKey:= w.privKey[:8] + preIV := w.privKey[8:16] + buf2 := new(bytes.Buffer); + w.desIV+=1; + binary.Write(buf2, binary.BigEndian, w.desIV) + privParam := string(buf.Bytes()) + string(buf2.Bytes()) + iv := strXor(preIV,privParam); + + //DES Encrypt + plen:=len(payload); + //padding + if (plen % 8) != 0 { + payload = payload + strings.Repeat("\x00",8-(plen%8)); + } + encrypted := make([]byte, len(payload)) + EncryptDESCBC(encrypted, []byte(payload), []byte(desKey), []byte(iv)) + return string(encrypted),privParam; } - return string(encrypted),privParam; } func (w WapSNMP) decrypt(payload,privParam string ) string { buf := new(bytes.Buffer); binary.Write(buf, binary.BigEndian, w.engineBoots) - buf2 := new(bytes.Buffer); - binary.Write(buf2, binary.BigEndian, w.engineTime) - iv := string(buf.Bytes()) + string(buf2.Bytes()) + privParam - // Decrypt - decrypted := make([]byte, len(payload)) - err := DecryptAESCFB(decrypted, []byte(payload), []byte(w.privKey), []byte(iv)) - if err != nil { - panic(err) + if w.privAlg == SNMP_AES { + buf2 := new(bytes.Buffer); + binary.Write(buf2, binary.BigEndian, w.engineTime) + iv := string(buf.Bytes()) + string(buf2.Bytes()) + privParam + + // Decrypt + decrypted := make([]byte, len(payload)) + err := DecryptAESCFB(decrypted, []byte(payload), []byte(w.privKey), []byte(iv)) + if err != nil { + panic(err) + } + return string(decrypted); + }else{ + desKey:= w.privKey[:8] + preIV := w.privKey[8:16] + iv := strXor(preIV,privParam); + + //DES Decrypt + plen:=len(payload); + if (plen % 8) != 0 { + panic("DES encrypted payload is not multiple of 8 bytes\n"); + } + decrypted := make([]byte, len(payload)) + DecryptDESCBC(decrypted, []byte(payload), []byte(desKey), []byte(iv)) + return string(decrypted); } - return string(decrypted); + } // GetNext issues a GETNEXT SNMP request. @@ -440,9 +508,14 @@ func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { w.engineID=v3HeaderDecoded[1].(string) w.engineBoots=int32(v3HeaderDecoded[2].(int)) w.engineTime=int32(v3HeaderDecoded[3].(int)) - //respAuthParam := v3HeaderDecoded[5].(string) + // skip checking authParam for now + respAuthParam := v3HeaderDecoded[5].(string) respPrivParam := v3HeaderDecoded[6].(string) + if len(respAuthParam) == 0 || len(respPrivParam) == 0 { + return nil, nil, fmt.Errorf("Error,response is not encrypted.") + } + encryptedResp := decodedResponse[4].(string); plainResp := w.decrypt(encryptedResp,respPrivParam); From e3cff264fa0bfdb2811c9acb1f2550dc2facd83c Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Thu, 7 Nov 2013 13:47:41 -0800 Subject: [PATCH 05/21] added the simple test file under utils --- utils/test.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 utils/test.go diff --git a/utils/test.go b/utils/test.go new file mode 100644 index 0000000..8dc6fca --- /dev/null +++ b/utils/test.go @@ -0,0 +1,9 @@ +package main +import ( + snmp "github.com/cdevr/WapSNMP" +) + +func main(){ + //snmp.DoWalkTest("127.0.0.1"); + snmp.DoWalkTestV3("127.0.0.1"); +} From c587503087aaf923f1a817c506cc2b0e2f16320f Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Fri, 29 Aug 2014 10:02:36 -0700 Subject: [PATCH 06/21] added trap receiver --- Makefile | 5 +++++ utils/trapd.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 Makefile create mode 100644 utils/trapd.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..65690dd --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +trapd: utils/trapd.go + go build utils/trapd.go +clean: + rm -f utils/trapd + diff --git a/utils/trapd.go b/utils/trapd.go new file mode 100644 index 0000000..cbd7b2d --- /dev/null +++ b/utils/trapd.go @@ -0,0 +1,53 @@ + +package main + +import ( + snmp "github.com/tiebingzhang/WapSNMP" + "log" + "net" + "math/rand" + "time" +) + +func myUDPServer(listenIPAddr string, port int) net.Conn { + addr := net.UDPAddr{ + Port: port, + IP: net.ParseIP(listenIPAddr), + } + conn, err := net.ListenUDP("udp", &addr) + if err != nil { + log.Printf("udp Listen error."); + panic(err) + } + return conn; +} + +func main() { + rand.Seed(0) + target := "" + community := "" + version := snmp.SNMPv2c + + udpsock := myUDPServer("0.0.0.0",162); + + wsnmp := snmp.NewWapSNMPOnConn(target, community, version, 2*time.Second, 5, udpsock) + defer wsnmp.Close() + packet:=make([]byte,3000); + for { + _,err:=udpsock.Read(packet); + if err!=nil{ + log.Fatal("udp read error\n"); + } + + val, err := wsnmp.ParseTrap(packet) + if err != nil { + log.Fatal("Error testing parsing v2 trap: %v.", err) + } + + if val != 0 { + log.Printf("Received wrong value : %v", val) + } + } + udpsock.Close(); + +} From f5e97be9afa9d3dc3343d4a1fb4b44ba40101630 Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Fri, 29 Aug 2014 10:06:39 -0700 Subject: [PATCH 07/21] added all trap changed --- ber.go | 2 +- ber_test.go | 10 +++++----- snmp.go | 18 ++++++++++++++++++ snmp_test.go | 30 ++++++++++++++++++++++++++++++ udp_stub_connection.go | 1 - utils/test.go | 3 ++- 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/ber.go b/ber.go index 5baaefb..5400270 100644 --- a/ber.go +++ b/ber.go @@ -254,7 +254,7 @@ func DecodeSequence(toparse []byte) ([]interface{}, error) { return nil, err } result = append(result, pdu) - case AsnGetNextRequest, AsnGetRequest, AsnGetResponse, AsnReport: + case AsnGetNextRequest, AsnGetRequest, AsnGetResponse, AsnReport, AsnTrap2: pdu, err := DecodeSequence(berAll) if err != nil { return nil, err diff --git a/ber_test.go b/ber_test.go index a28ecf8..65d4f2d 100644 --- a/ber_test.go +++ b/ber_test.go @@ -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 { diff --git a/snmp.go b/snmp.go index 4cfda2f..ce352cb 100644 --- a/snmp.go +++ b/snmp.go @@ -636,6 +636,24 @@ func (w WapSNMP) GetTable(oid Oid) (map[string]interface{}, error) { return result, nil } +// ParseTrap parses a received SNMP trap and returns a map of oid to objects +func (w WapSNMP) ParseTrap(response []byte) (interface{}, error) { + decodedResponse, err := DecodeSequence(response) + if err != nil { + return 1, err + } + fmt.Printf("%#v\n",decodedResponse); + + // Fetch the varbinds out of the packet. + respPacket := decodedResponse[3].([]interface{}) + varbinds := respPacket[4].([]interface{}) + result1 := varbinds[1].([]interface{})[2] + result2 := varbinds[2].([]interface{})[2] + fmt.Printf("v1=%s, v2=%d\n",result1,result2); + + return 0, nil +} + // Close the net.conn in WapSNMP. func (w WapSNMP) Close() error { return w.conn.Close() diff --git a/snmp_test.go b/snmp_test.go index 7d3dec5..3d2bbf3 100644 --- a/snmp_test.go +++ b/snmp_test.go @@ -5,6 +5,7 @@ import ( "math/rand" // Needed to set Seed, so a consistent request ID will be chosen. "testing" "time" + "encoding/hex" ) func ExampleGetTable() { @@ -102,6 +103,7 @@ func ExampleGet() { func TestGet(t *testing.T) { rand.Seed(0) + target := "magic_host" community := "[R0_C@cti!]" version := SNMPv2c @@ -127,3 +129,31 @@ func TestGet(t *testing.T) { } } + +func TestTrapV2(t *testing.T) { + rand.Seed(0) + target := "magic_host" + community := "public" + version := SNMPv2c + + //oid := MustParseOid("1.2.3.4.0") + udpStub := NewUdpStub(t) + defer udpStub.CheckClosed() + wsnmp := NewWapSNMPOnConn(target, community, version, 2*time.Second, 5, udpStub) + defer wsnmp.Close() + + packet,err:=hex.DecodeString("304302010104067075626c6963a73602047cd94c540201000201003028301006082b0601020101030043043aa3e6303014060a2b06010603010104010006062b0601020100") + if err != nil { + t.Fatalf("Error while decoding trap packet : '%v'", err) + } + + val, err := wsnmp.ParseTrap(packet) + if err != nil { + t.Errorf("Error testing parsing v2 trap: %v.", err) + } + + if val != 0 { + t.Errorf("Received wrong value : %v", val) + } + +} diff --git a/udp_stub_connection.go b/udp_stub_connection.go index 48f14c4..e2d173d 100644 --- a/udp_stub_connection.go +++ b/udp_stub_connection.go @@ -24,7 +24,6 @@ type expectAndRespond struct { readability). */ type udpStub struct { - expectedPacketsAndResponses map[string][]byte ignoreUnknownPackets bool expectResponses []*expectAndRespond queuedPackets []string diff --git a/utils/test.go b/utils/test.go index 8dc6fca..c425d1f 100644 --- a/utils/test.go +++ b/utils/test.go @@ -1,6 +1,7 @@ package main import ( - snmp "github.com/cdevr/WapSNMP" + //snmp "github.com/cdevr/WapSNMP" + snmp "github.com/tiebingzhang/WapSNMP" ) func main(){ From 9bb6d8a84cb2d24de3d8f8656ff27b811809fc7b Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 10:39:19 -0700 Subject: [PATCH 08/21] trapd is working for v2 --- Makefile | 4 ++-- snmp.go | 17 +++++++++++++---- utils/trapd.go | 6 ++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 65690dd..d643d96 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -trapd: utils/trapd.go +trapd: utils/trapd.go snmp.go go build utils/trapd.go clean: - rm -f utils/trapd + rm -f trapd diff --git a/snmp.go b/snmp.go index ce352cb..54f6a63 100644 --- a/snmp.go +++ b/snmp.go @@ -642,14 +642,23 @@ func (w WapSNMP) ParseTrap(response []byte) (interface{}, error) { if err != nil { return 1, err } - fmt.Printf("%#v\n",decodedResponse); + var community string; // Fetch the varbinds out of the packet. + snmpVer:= decodedResponse[1].(int)+1; + fmt.Printf("Version:%d\n",snmpVer); + if (snmpVer<3){ + community = decodedResponse[2].(string); + fmt.Printf("Community:%s\n",community); + } respPacket := decodedResponse[3].([]interface{}) varbinds := respPacket[4].([]interface{}) - result1 := varbinds[1].([]interface{})[2] - result2 := varbinds[2].([]interface{})[2] - fmt.Printf("v1=%s, v2=%d\n",result1,result2); + for i:=1;i Date: Fri, 29 Aug 2014 12:29:22 -0700 Subject: [PATCH 09/21] added v3 trap decryption and an user table --- snmp.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++--- utils/trapd.go | 13 ++++----- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/snmp.go b/snmp.go index 54f6a63..2f04d7a 100644 --- a/snmp.go +++ b/snmp.go @@ -17,8 +17,17 @@ import ( "strings" "bytes" "encoding/binary" + "errors" ) +type V3user struct { + User string + AuthAlg string //MD5 or SHA1 + AuthPwd string + PrivAlg string //AES or DES + PrivPwd string +} + // The object type that lets you do SNMP requests. type WapSNMP struct { Target string // Target device for these SNMP events. @@ -41,6 +50,7 @@ type WapSNMP struct { engineTime int32 desIV uint32 aesIV int64 + Trapusers []V3user } const ( @@ -637,20 +647,81 @@ func (w WapSNMP) GetTable(oid Oid) (map[string]interface{}, error) { } // ParseTrap parses a received SNMP trap and returns a map of oid to objects -func (w WapSNMP) ParseTrap(response []byte) (interface{}, error) { +func (w WapSNMP) ParseTrap(response []byte) error { decodedResponse, err := DecodeSequence(response) if err != nil { - return 1, err + return err } var community string; // Fetch the varbinds out of the packet. - snmpVer:= decodedResponse[1].(int)+1; + snmpVer:= decodedResponse[1].(int); + if snmpVer<=1 { + snmpVer++; + } fmt.Printf("Version:%d\n",snmpVer); if (snmpVer<3){ community = decodedResponse[2].(string); fmt.Printf("Community:%s\n",community); + }else{ + /* + for i, val := range decodedResponse{ + fmt.Printf("Resp:%v:type=%v\n",i,reflect.TypeOf(val)); + } + */ + v3HeaderStr := decodedResponse[3].(string); + v3HeaderDecoded, err := DecodeSequence([]byte(v3HeaderStr)) + if err != nil { + fmt.Printf("Error 2 decoding:%v\n",err); + return err; + } + + w.engineID=v3HeaderDecoded[1].(string) + w.engineBoots=int32(v3HeaderDecoded[2].(int)) + w.engineTime=int32(v3HeaderDecoded[3].(int)) + w.user =v3HeaderDecoded[4].(string) + respAuthParam := v3HeaderDecoded[5].(string) + respPrivParam := v3HeaderDecoded[6].(string) + fmt.Printf("username=%s\n",w.user); + + if len(respAuthParam) == 0 || len(respPrivParam) == 0 { + return errors.New("response is not encrypted"); + } + if len(w.Trapusers)==0 { + return errors.New("No SNMP V3 trap user configured"); + } + + founduser := false; + for _,v3user := range w.Trapusers { + if v3user.User==w.user { + w.authAlg=v3user.AuthAlg; + w.privAlg=v3user.PrivAlg; + w.authPwd=v3user.AuthPwd; + w.privPwd=v3user.PrivPwd; + founduser = true; + break; + } + } + if !founduser { + return errors.New("No matching user found"); + } + + //keys + w.authKey = password_to_key(w.authPwd, w.engineID , w.authAlg); + privKey := password_to_key(w.privPwd, w.engineID , w.authAlg); + w.privKey = string(([]byte(privKey))[0:16]) + + encryptedResp := decodedResponse[4].(string); + plainResp := w.decrypt(encryptedResp,respPrivParam); + + pduDecoded, err := DecodeSequence([]byte(plainResp)) + if err != nil { + fmt.Printf("Error 3 decoding:%v\n",err); + return err; + } + decodedResponse=pduDecoded; } + respPacket := decodedResponse[3].([]interface{}) varbinds := respPacket[4].([]interface{}) for i:=1;i Date: Fri, 29 Aug 2014 12:31:24 -0700 Subject: [PATCH 10/21] fixed go test --- snmp_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/snmp_test.go b/snmp_test.go index 3d2bbf3..c9d7d20 100644 --- a/snmp_test.go +++ b/snmp_test.go @@ -147,13 +147,8 @@ func TestTrapV2(t *testing.T) { t.Fatalf("Error while decoding trap packet : '%v'", err) } - val, err := wsnmp.ParseTrap(packet) + err = wsnmp.ParseTrap(packet) if err != nil { t.Errorf("Error testing parsing v2 trap: %v.", err) } - - if val != 0 { - t.Errorf("Received wrong value : %v", val) - } - } From 95de2b30effe0ee601c58775e792ee9acd7c8a24 Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 12:42:49 -0700 Subject: [PATCH 11/21] updated README --- README.md | 48 +++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index d0fc305..0fe9e9f 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,26 @@ WapSnmp : SNMP client for golang -------------------------------- +Currently supported operations: +SNMP v1, v2c Get, GetMultiple, GetNext, GetBulk, Walk +SNMP V3 Walk +SNMP V2c/v3 trap receiver with EngineID auto discovery -# This fork seeks to add SNMP V3 capability -# Original README -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). +Not supported yet: +SNMP Set +SNMP Informs receiver +SNMP V3 Get, GetMultiple, GetBulk (these can be easily implemented since SNMP V3 Walk is working) -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. - -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. - -It has been tested on juniper and cisco devices and has proven to remain stable over long periods of time. - -Example: +Compile +-------------------------------- +go build utils/test.go will build the test program for SNMP v3 walk +go build utils/trapd.go will build the trapd program, which is able to receive SNMP v2 and v3 traps (you need to configure +users for SNMP v3 traps) - func DoGetTableTest(target string) { - community := "public" - version := SNMPv2c +You can run "go test" to perform unit test. - oid := MustParseOid(".1.3.6.1.4.1.2636.3.2.3.1.20") +Using the code +--------------------------------- +The *_test.go files provide good examples of how to use these functions - 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. From a9408e6fb0d3e533d2a4d7fc75c2bfba0fa1268f Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 12:44:11 -0700 Subject: [PATCH 12/21] updated README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0fe9e9f..3d2a859 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ WapSnmp : SNMP client for golang -------------------------------- Currently supported operations: -SNMP v1, v2c Get, GetMultiple, GetNext, GetBulk, Walk -SNMP V3 Walk -SNMP V2c/v3 trap receiver with EngineID auto discovery +* SNMP v1, v2c Get, GetMultiple, GetNext, GetBulk, Walk +* SNMP V3 Walk +* SNMP V2c/v3 trap receiver with EngineID auto discovery Not supported yet: -SNMP Set -SNMP Informs receiver -SNMP V3 Get, GetMultiple, GetBulk (these can be easily implemented since SNMP V3 Walk is working) +* SNMP Set +* SNMP Informs receiver +* SNMP V3 Get, GetMultiple, GetBulk (these can be easily implemented since SNMP V3 Walk is working) Compile -------------------------------- From a9048ee1455af9026a84e2568f76fb72ced8f5b5 Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 12:44:51 -0700 Subject: [PATCH 13/21] updated README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3d2a859..c147b6b 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ Not supported yet: Compile -------------------------------- -go build utils/test.go will build the test program for SNMP v3 walk -go build utils/trapd.go will build the trapd program, which is able to receive SNMP v2 and v3 traps (you need to configure +* go build utils/test.go will build the test program for SNMP v3 walk +* go build utils/trapd.go will build the trapd program, which is able to receive SNMP v2 and v3 traps (you need to configure users for SNMP v3 traps) You can run "go test" to perform unit test. From d66c0fc4fecfd641eb516ca250ef11fd00e4ae4f Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 17:32:27 -0700 Subject: [PATCH 14/21] added Counter64 support to ber.go; updated snmpwalk --- Makefile | 8 ++++++-- ber.go | 19 +++++++++++++++++++ example.go | 8 +++----- utils/test.go | 2 +- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index d643d96..7c4facb 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,9 @@ -trapd: utils/trapd.go snmp.go +all:gowalk +trapd: utils/trapd.go snmp.go ber.go go build utils/trapd.go +gowalk: utils/test.go snmp.go ber.go + go build utils/test.go + mv -f test gowalk clean: - rm -f trapd + rm -f trapd gowalk diff --git a/ber.go b/ber.go index 5400270..641f42f 100644 --- a/ber.go +++ b/ber.go @@ -145,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. */ @@ -242,6 +254,13 @@ func DecodeSequence(toparse []byte) ([]interface{}, error) { 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 { diff --git a/example.go b/example.go index 5e05bf5..c676eb2 100644 --- a/example.go +++ b/example.go @@ -57,14 +57,12 @@ func DoWalkTest(target string) { } } -func DoWalkTestV3(target string) { - oid := MustParseOid(".1.3.6.1.2.1.1") +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, "tzhang",SNMP_SHA1,"1234567890",SNMP_AES,"1234567890", 2*time.Second, 2) - //wsnmp, err := NewWapSNMPv3(target, "tzhang_ma",SNMP_MD5,"1234567890",SNMP_AES,"1234567890", 2*time.Second, 2) - wsnmp, err := NewWapSNMPv3(target, "tzhang_md",SNMP_MD5,"1234567890",SNMP_DES,"1234567890", 2*time.Second, 2) + 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 diff --git a/utils/test.go b/utils/test.go index c425d1f..d807d8f 100644 --- a/utils/test.go +++ b/utils/test.go @@ -6,5 +6,5 @@ import ( func main(){ //snmp.DoWalkTest("127.0.0.1"); - snmp.DoWalkTestV3("127.0.0.1"); + snmp.DoWalkTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1", "hcm.snmpv3","SHA1","this_is_my_hcm","AES", "my_hcm_is_4_me"); } From 50ce5ba150b56028881cab91e8b32c893d418622 Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 17:33:03 -0700 Subject: [PATCH 15/21] renamed test to gowalk --- utils/{test.go => gowalk.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils/{test.go => gowalk.go} (100%) diff --git a/utils/test.go b/utils/gowalk.go similarity index 100% rename from utils/test.go rename to utils/gowalk.go From 8e8e2492986dc2e86e89fac43236d8c3bee56dd0 Mon Sep 17 00:00:00 2001 From: tzhang Date: Fri, 29 Aug 2014 17:35:19 -0700 Subject: [PATCH 16/21] added windows build; rename test to gowalk --- Makefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 7c4facb..3918ccf 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,15 @@ all:gowalk trapd: utils/trapd.go snmp.go ber.go go build utils/trapd.go -gowalk: utils/test.go snmp.go ber.go - go build utils/test.go - mv -f test gowalk +gowalk: utils/gowalk.go snmp.go ber.go + go build utils/gowalk.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 + clean: - rm -f trapd gowalk + rm -f trapd gowalk *.exe From e6930f9b7e970d0578bc0111a64974a2dca5cbea Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Fri, 29 Aug 2014 18:12:57 -0700 Subject: [PATCH 17/21] added SNMP get for v3 and a goget program --- Makefile | 8 ++++++-- README.md | 20 +++++++++++++------- example.go | 19 +++++++++++++++++++ snmp.go | 13 ++++++++++++- utils/goget.go | 8 ++++++++ utils/gowalk.go | 2 +- 6 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 utils/goget.go diff --git a/Makefile b/Makefile index 3918ccf..c5b6fa7 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,19 @@ -all:gowalk +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 *.exe + rm -f trapd gowalk goget *.exe diff --git a/README.md b/README.md index c147b6b..ad683c6 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,32 @@ WapSnmp : SNMP client for golang -------------------------------- Currently supported operations: -* SNMP v1, v2c Get, GetMultiple, GetNext, GetBulk, Walk -* SNMP V3 Walk +* SNMP v1/v2c Get, GetMultiple, GetNext, GetBulk, Walk +* SNMP V3 Get, Walk, GetNext * SNMP V2c/v3 trap receiver with EngineID auto discovery Not supported yet: * SNMP Set * SNMP Informs receiver -* SNMP V3 Get, GetMultiple, GetBulk (these can be easily implemented since SNMP V3 Walk is working) +* SNMP V3 GetMultiple, GetBulk (these can be easily implemented since SNMP V3 Walk/Get/GetNext is working) Compile -------------------------------- -* go build utils/test.go will build the test program for SNMP v3 walk -* go build utils/trapd.go will build the trapd program, which is able to receive SNMP v2 and v3 traps (you need to configure -users for SNMP v3 traps) +make + +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) + You can run "go test" to perform unit test. Using the code --------------------------------- -The *_test.go files provide good examples of how to use these functions +* 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. + diff --git a/example.go b/example.go index c676eb2..1baf2cf 100644 --- a/example.go +++ b/example.go @@ -112,4 +112,23 @@ func DoGetTest(target string) { } } +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) +} diff --git a/snmp.go b/snmp.go index 2f04d7a..bc1b776 100644 --- a/snmp.go +++ b/snmp.go @@ -459,11 +459,22 @@ func (w WapSNMP) decrypt(payload,privParam string ) string { // GetNext issues a GETNEXT SNMP request. func (w *WapSNMP) GetNextV3(oid Oid) (*Oid, interface{}, error) { + return w.doGetV3(oid,AsnGetNextRequest); +} + +// Get +func (w *WapSNMP) GetV3(oid Oid) (interface{}, error) { + _,val,err:=w.doGetV3(oid,AsnGetRequest); + return val,err; +} + +// A function does both GetNext and Get for SNMP V3 +func (w *WapSNMP) doGetV3(oid Oid, request BERType) (*Oid, interface{}, error) { msgID := getRandomRequestID() requestID := getRandomRequestID() req, err := EncodeSequence( []interface{}{Sequence,w.engineID,"", - []interface{}{AsnGetNextRequest, requestID, 0, 0, + []interface{}{request, requestID, 0, 0, []interface{}{Sequence, []interface{}{Sequence, oid, nil}}}}) if err != nil { diff --git a/utils/goget.go b/utils/goget.go new file mode 100644 index 0000000..94185db --- /dev/null +++ b/utils/goget.go @@ -0,0 +1,8 @@ +package main +import ( + snmp "github.com/tiebingzhang/WapSNMP" +) + +func main(){ + snmp.DoGetTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1.1.6.0", "hcm.snmpv3","SHA1","this_is_my_hcm","AES", "my_hcm_is_4_me"); +} diff --git a/utils/gowalk.go b/utils/gowalk.go index d807d8f..76962a7 100644 --- a/utils/gowalk.go +++ b/utils/gowalk.go @@ -6,5 +6,5 @@ import ( func main(){ //snmp.DoWalkTest("127.0.0.1"); - snmp.DoWalkTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1", "hcm.snmpv3","SHA1","this_is_my_hcm","AES", "my_hcm_is_4_me"); + snmp.DoWalkTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1.1", "hcm.snmpv3","SHA1","this_is_my_hcm","AES", "my_hcm_is_4_me"); } From 708807ebb26f44f8b82fd2379eadcd950f0ba15b Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Fri, 29 Aug 2014 18:45:47 -0700 Subject: [PATCH 18/21] added snmp v1 trap parse support --- ber.go | 16 +++++++++++++++- snmp.go | 12 +++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ber.go b/ber.go index 641f42f..0b54189 100644 --- a/ber.go +++ b/ber.go @@ -171,6 +171,14 @@ 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 { if toEncode==0 { @@ -267,13 +275,19 @@ func DecodeSequence(toparse []byte) ([]interface{}, error) { 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, AsnReport, AsnTrap2: + case AsnGetNextRequest, AsnGetRequest, AsnGetResponse, AsnReport, AsnTrap2, AsnTrap: pdu, err := DecodeSequence(berAll) if err != nil { return nil, err diff --git a/snmp.go b/snmp.go index bc1b776..ffb5f00 100644 --- a/snmp.go +++ b/snmp.go @@ -732,9 +732,19 @@ func (w WapSNMP) ParseTrap(response []byte) error { } decodedResponse=pduDecoded; } + //fmt.Printf("%#v\n",decodedResponse); respPacket := decodedResponse[3].([]interface{}) - varbinds := respPacket[4].([]interface{}) + var varbinds []interface{} + if (snmpVer==1){ + fmt.Printf("OID: %s\n",respPacket[1]) + fmt.Printf("Agent Address: %s\n",respPacket[2]) + fmt.Printf("Generic Trap: %d\n",respPacket[3]) + varbinds = respPacket[6].([]interface{}) + }else{ + varbinds = respPacket[4].([]interface{}) + } + for i:=1;i Date: Fri, 29 Aug 2014 18:47:25 -0700 Subject: [PATCH 19/21] updated README --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ad683c6..c249777 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,9 @@ 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 -* SNMP V2c/v3 trap receiver with EngineID auto discovery - -Not supported yet: -* SNMP Set -* SNMP Informs receiver -* SNMP V3 GetMultiple, GetBulk (these can be easily implemented since SNMP V3 Walk/Get/GetNext is working) Compile -------------------------------- @@ -16,10 +11,9 @@ make This will compile the following binaries: * goget : get single SNMP mib using SNMP v3 -* gowalk : walk SNMP mibs 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) - You can run "go test" to perform unit test. Using the code @@ -27,6 +21,11 @@ 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. +Not supported yet: +------------------ +* SNMP Informs receiver +* SNMP v3 GetMultiple, GetBulk (these can be easily implemented since SNMP v3 Walk/Get/GetNext is working) + From d7719f41a8cf9154ac10710e3558d2b500133953 Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Fri, 29 Aug 2014 18:50:23 -0700 Subject: [PATCH 20/21] added tools --- tools/sendtrap_v1.sh | 1 + tools/sendtrap_v2.sh | 2 ++ tools/sendtrap_v3.sh | 1 + 3 files changed, 4 insertions(+) create mode 100755 tools/sendtrap_v1.sh create mode 100755 tools/sendtrap_v2.sh create mode 100755 tools/sendtrap_v3.sh diff --git a/tools/sendtrap_v1.sh b/tools/sendtrap_v1.sh new file mode 100755 index 0000000..0c2d896 --- /dev/null +++ b/tools/sendtrap_v1.sh @@ -0,0 +1 @@ +snmptrap -v 1 -c public 192.168.5.203 1.2.3.4 192.168.5.201 3 0 '' 1.3.5.6.7 s "abc" 1.3.5.6.8 s "def" diff --git a/tools/sendtrap_v2.sh b/tools/sendtrap_v2.sh new file mode 100755 index 0000000..9432fde --- /dev/null +++ b/tools/sendtrap_v2.sh @@ -0,0 +1,2 @@ +snmptrap -v 2c -c public 192.168.5.203 "" 1.3.6.1.2.1.0 1.3.6.7.8.9. s "abc" + diff --git a/tools/sendtrap_v3.sh b/tools/sendtrap_v3.sh new file mode 100755 index 0000000..54e2aee --- /dev/null +++ b/tools/sendtrap_v3.sh @@ -0,0 +1 @@ +snmptrap -v 3 -a SHA -A this_is_my_hcm -x AES -X my_hcm_is_4_me -l authPriv -u hcm.snmpv3 -e 0x80001f8880315de44d53ce8394 192.168.5.200 "" linkUp.0 1.3.6.4.5.6 s "switch is off" From 92cdb4c137874f2426fc757bb80af73458f43a3b Mon Sep 17 00:00:00 2001 From: Tiebing Zhang Date: Fri, 29 Aug 2014 18:54:38 -0700 Subject: [PATCH 21/21] const string update --- tools/sendtrap_v3.sh | 2 +- utils/goget.go | 2 +- utils/gowalk.go | 2 +- utils/trapd.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/sendtrap_v3.sh b/tools/sendtrap_v3.sh index 54e2aee..9ee85d2 100755 --- a/tools/sendtrap_v3.sh +++ b/tools/sendtrap_v3.sh @@ -1 +1 @@ -snmptrap -v 3 -a SHA -A this_is_my_hcm -x AES -X my_hcm_is_4_me -l authPriv -u hcm.snmpv3 -e 0x80001f8880315de44d53ce8394 192.168.5.200 "" linkUp.0 1.3.6.4.5.6 s "switch is off" +snmptrap -v 3 -a SHA -A this_is_my_pcb -x AES -X my_pcb_is_4_me -l authPriv -u pcb.snmpv3 -e 0x80001f8880315de44d53ce8394 192.168.5.200 "" linkUp.0 1.3.6.4.5.6 s "switch is off" diff --git a/utils/goget.go b/utils/goget.go index 94185db..fb557bb 100644 --- a/utils/goget.go +++ b/utils/goget.go @@ -4,5 +4,5 @@ import ( ) func main(){ - snmp.DoGetTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1.1.6.0", "hcm.snmpv3","SHA1","this_is_my_hcm","AES", "my_hcm_is_4_me"); + snmp.DoGetTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1.1.6.0", "pcb.snmpv3","SHA1","this_is_my_pcb","AES", "my_pcb_is_4_me"); } diff --git a/utils/gowalk.go b/utils/gowalk.go index 76962a7..464d625 100644 --- a/utils/gowalk.go +++ b/utils/gowalk.go @@ -6,5 +6,5 @@ import ( func main(){ //snmp.DoWalkTest("127.0.0.1"); - snmp.DoWalkTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1.1", "hcm.snmpv3","SHA1","this_is_my_hcm","AES", "my_hcm_is_4_me"); + snmp.DoWalkTestV3("10.0.217.204","1.3.6.1.4.1.27822.8.1.1", "pcb.snmpv3","SHA1","this_is_my_pcb","AES", "my_pcb_is_4_me"); } diff --git a/utils/trapd.go b/utils/trapd.go index 8e1fc61..5b9720a 100644 --- a/utils/trapd.go +++ b/utils/trapd.go @@ -33,7 +33,7 @@ func main() { wsnmp := snmp.NewWapSNMPOnConn(target, community, version, 2*time.Second, 5, udpsock) defer wsnmp.Close() - wsnmp.Trapusers = append(wsnmp.Trapusers,snmp.V3user{ "hcm.snmpv3","SHA1","this_is_my_hcm","AES","my_hcm_is_4_me" }); + wsnmp.Trapusers = append(wsnmp.Trapusers,snmp.V3user{ "pcb.snmpv3","SHA1","this_is_my_pcb","AES","my_pcb_is_4_me" }); packet:=make([]byte,3000); for {