MJ - Because SNMP uses ASN.1 to encode packets, one can use tcllib's asn package to decode the SNMP packets. The code below implements the ::asn::decodeBer command that will create a nested list of the parsed SNMP packet.
package require asn namespace eval asn { namespace eval util { proc string2hex {string} { binary scan $string H* t set res [regexp -inline -all {..} $t] return [join $res " "] } } # array with the implicit tags definitions # format: # tag {type {implicit tag} {decoding proc}} # this should really be stored in a tree-like structure array set tags { 02 { INTEGER {} asnGetInteger} 04 { OCTETSTRING {} asnGetOctetString} 05 { NULL {} decodeNull} 06 { {OBJECT IDENTIFIER} {} asnGetObjectIdentifier} 30 { SEQUENCE {} decodeSequence} 40 { NetworkAddress {} asnGetNetworkAddress} 41 { Counter32 02 {}} 43 { TimeTicks 02 {}} A0 { GetRequest-PDU 30 {}} A1 { GetNextRequest-PDU 30 {}} A2 { GetResponse-PDU 30 {}} A3 { SetRequest-PDU 30 {}} A4 { Trap-PDU 30 {}} A6 { InformRequest-PDU 30 {}} A7 { SNMPv2-Trap-PDU 30 {}} } proc decodeBer {raw} { variable tags asnPeekByte raw tag set tag [format %02X $tag] if {[info exists tags($tag)] } { set tagInfo $tags($tag) set type [lindex $tagInfo 0] set implicitTag [lindex $tagInfo 1] while {$implicitTag ne {}} { # if we are handling an implicit type, # find the corresponding type it derived from that has a decode proc set tagInfo $tags($implicitTag) if {[lindex $tagInfo end] ne {}} { asnRetag raw [expr "0x$implicitTag"] break } set implicitTag [lindex $tagInfo 1] } [lindex $tagInfo end] raw res return [list $type $res] } else { asnGetByte raw tag asnGetLength raw length asnGetBytes raw $length value return [list $tag [util::string2hex $value] ] } } proc asnGetTLV { data_var tlv_var } { # gets a tag/length/value from the start of the data. # return false if no data available, true if succesful upvar $data_var data $tlv_var tlv if {[string length $data] < 2} { return false } asnGetByte data tag asnGetLength data length asnGetBytes data $length temp set tlv [asnTLV $tag $temp] return true } proc asnTLV {tag value} { set len [string length $value] return [binary format H2a*a$len [format %02X $tag] [asnLength $len] $value] } proc decodeSequence {data res} { upvar $data raw upvar $res result asnGetSequence raw sequence while {[asnGetTLV sequence element]} { lappend result [decodeBer $element] } return } proc decodeNull {data res} { # we need this proc because asnGetNull takes only one argument upvar $data raw upvar $res result asnGetNull raw set result {} return } proc asnGetNetworkAddress {data res} { upvar $data raw upvar $res result asnGetByte raw dummy asnGetByte raw length asnGetBytes raw $length address foreach number [split $address ""] { lappend result [scan $number %c] } return } } package provide ber 0.1
Sample usage for an SNMPv1 trap PDU
package require base64 set trap [join {MIGXAgEABAZwdWJsaWOkgYkGCCsGAQQBgo17QATAqAAzAgEGAgID6EMBZDBtMBoGDCsFAQQBgo17?\ AQbOEAQKbG9jYWxob3N0IDBPBgwrBQEEAYKNewEGzhEEPyBzdShwYW1fdW5peClbMjUxMTVdOiBz?\ ZXNzaW9uIG9wZW5lZCBmb3IgdXNlciByb290IGJ5ICh1aWQ9NTAwKQ==} {}] ::asn::decodeBer [base64::decode $trap]
gives:
SEQUENCE {{INTEGER 0} {OCTETSTRING public} {Trap-PDU {{{OBJECT IDENTIFIER} {1 3 6 1 4 1 34555}}\ {IpAddress {192 168 0 51}} {INTEGER 6} {INTEGER 1000} {TimeTicks 100}\ {SEQUENCE {{SEQUENCE {{{OBJECT IDENTIFIER} {1 3 5 1 4 1 34555 1 6 10000}} {OCTETSTRING {localhost }}}}\ {SEQUENCE {{{OBJECT IDENTIFIER} {1 3 5 1 4 1 34555 1 6 10001}}\ {OCTETSTRING { su(pam_unix)[25115]: session opened for user root by (uid=500)}}}}}}}}}
MJ - 14-03-2007: Removed a lot of the redundancy when handling the implicit types.
Category Parsing | Category Example | Category Networking | Category Package |
---|