SNMP parser

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