Updated 2008-07-18 15:39:46 by AK

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