2008年11月13日 星期四

用 Tcl 實作發送 Snmptrap

其實我還不是很熟 Snmptrap 啦 ... 只是在網路上看到了一篇 PPT ,就試著實作看看。

參考的網址如下:
http://www.chu.edu.tw/~hsiang/91_Class/NM2.ppt


符合 BER 格式的封包內容像這樣 ... (這算是補充 PTT 沒有的部份)



底下就是一份實作的範例

snmptrap.tcl
# Snmptrap 封包內容共三段
# Version + Community + Data(SNMP Protocol Data Unit (SNMP PDU))

# ------------------------------------
#            |Type
# ------------------------------------
# Version    |INTEGER {version-1(0)}
# Community  |OCTET STRING
# Data       |ANY (Sequence)
# ------------------------------------


# 其中 Data 又分為兩段
# PDU Header + PDU Data


# PDU Header 又分五段
# Enterprise + Agent address + Generic trap type + Specific trap code + Time stamp

# Enterprise—Identifies the type of managed object that generates the trap.
# Agent address—Provides the address of the managed object that generates the trap.
# Generic trap type—Indicates one of a number of generic trap types.
# Specific trap code—Indicates one of a number of specific trap codes.
# Time stamp—Provides the amount of time that has elapsed between the last network reinitialization and generation of the trap.

# ------------------------------------
#                       |Type
# ------------------------------------
# Enterprise            |ObjectIdentifier
# Agent address         |Hex
# Generic trap type     |INTEGER
# Specific trap code    |INTEGER
# Time stamp            |IntegerOrEnum
# ------------------------------------



# PDU Data 為一份序列(Sequence),序列內容可以包含一個以上的序列,結構如下
# [[OID + Value][OID + Value][OID + Value] ... [OID + Value]]



# ------------------------
# For demo send trap ...

# ----------------------
# import package
# ----------------------


package require asn
package require udp
package require ip


# -------------------------
# utility procedures
# -------------------------

# Figure out what this machine's IP address is.
# Refernce http:
//wiki.tcl.tk/3015 to rewrite this proc
proc myip {} {
    
set myip "127.0.0.1"
    
switch $::tcl_platform(platform) {
        
unix {
            
set buffer [exec /sbin/ifconfig]
            
set scan "inet addr:"
            
foreach line [split $buffer \n] {
                
if ![regexp $scan $line] continue
                
set s [expr [string first $scan $line 0] + [string length $scan]]
                
set f [expr [string first " " $line $s] - 1]
                
set addr [string range $line $s $f]
                
if { $addr != "127.0.0.1" } {
                    
set myip $addr
                    
break
                
}
            
}
        
}
        
windows {
            
catch { exec ipconfig } data
            
set buffer [split $data \n]
            
foreach line $buffer {
                   
if [regexp -nocase {IP Address} $line] {
                    
set myip [string trim [lindex [split $line :] end]]
                    
break
                
}
            
}
        
}
    
}
    
return $myip
}

proc myHexIP {} {
    
return [string range [ip::toHex [myip]] 2 end]
}


proc send_trap {args} {
    
# ---------------------
    # Configuration
    # ---------------------

    
# send trap to ... ?
    set server 127.0.0.1
    
set port 162

    
# 0 | 1
    set version 0

    
# public | password
    set community public

    
# 1.3.6.1.4.1.23041
    set enterprise "1 3 6 1 4 1 23041"

    
set agent_address [myHexIP]

    
set generic_trap_type 6

    
set specific_trap_code 0

    
# --------------------------------
    # start for encoding

    
set timestamp [clock second]


    
# Version
    set packet [asn::asnInteger $version]

    
# Version + Community
    append packet [asn::asnOctetString $community]

    
# PDU Header = Enterprise + Agent address + Generic trap type + Specific trap code + Time stamp
    set PDU_Header [asn::asnObjectIdentifier $enterprise]
    
append PDU_Header [binary format H* "4004$agent_address"]
    
append PDU_Header [asn::asnInteger $generic_trap_type]
    
append PDU_Header [asn::asnInteger $specific_trap_code]
    
append PDU_Header [asn::asnIntegerOrEnum 43 [expr ([clock seconds] - $timestamp) * 100]]

    
# PDU Data = [[OID + Value][OID + Value][OID + Value] ... [OID + Value]]
    set PDU_Data ""

    
foreach {OID value} $args {
        
set sequence [asn::asnObjectIdentifier $OID]
        
append sequence [asn::asnOctetString [encoding convertto $value]]
        
        
# 將 OID + Value 編成一個序列
        set sequence [asn::asnSequence $sequence]
        
        
# 放入 PDU_Data 主序列中
        append PDU_Data $sequence
    
}

    
# 把 PDU_Data 正式編成一個序列
    set PDU_Data [asn::asnSequence $PDU_Data]


    
# SNMP PDU = PDU Header + PDU Data
    set SNMP_PDU [format {%s%s} $PDU_Header $PDU_Data]

    
# OCTET STRING (0x04)
    set SNMP_PDU [asn::asnChoiceConstr 4 $SNMP_PDU]

    
# Version + Community + Data(SNMP Protocol Data Unit (SNMP PDU))
    append packet $SNMP_PDU

    
# 把 packet 正式編成一個序列
    set packet [asn::asnSequence $packet]




    
# 發送 trap / udp
    set s [udp_open]
    
fconfigure $s -blocking 0 -remote [list $server $port] -translation binary -encoding binary
    
puts -nonewline $s $packet
    
flush $s
    
close $s
}


接下來只要利用 send_trap 這個 proc 並傳兩個參數(OID、value),就可以打開接收器看成果了!

沒有留言: