SNMP v2c の trap (coldStart) を送信するperlスクリプト sendTrap.pl を作成した。perl sendTrap.pl として実行すると、localhost の 162/udp に対して、coldStartトラップを送信する。例えば、SNMPマネージャのtrap受信テストを簡単に行うのに使用できると思う。
use strict; use warnings; use Socket; #--------------- # SNMP type #--------------- my $V1_TRAP = 'a4'; my $V2_TRAP = 'a7'; #--------------- # Data type #--------------- my $INTEGER = '02'; my $OCTET_STRING = '04'; my $OBJECT_ID = '06'; my $SEQUENCE = '30'; my $IP_ADDRESS = '40'; my $TIME_TICKS = '43'; #--------------- # Dest info #--------------- my $DEST_ADDR = 'localhost'; my $DEST_PORT = 162; #--------------- # MAIN #--------------- my $socket; socket($socket, PF_INET, SOCK_DGRAM, 0) or die "Failed to create socket. $!"; my $addrBin = inet_aton($DEST_ADDR); my $sockAddr = pack_sockaddr_in($DEST_PORT, $addrBin); my $trapData = constractTrapData(); my $trapDataBin = pack("H*", $trapData); send($socket, $trapDataBin, 0, $sockAddr) or die "Failed to send. $!"; #--------------- # Sub functions #--------------- sub constractTrapData { my $snmpVersion = 1; # v2c my $commName = 'public'; my $pduType = 'a7'; # SNMPv2 trap my $requestID = 10; my $errorStatus = 0; my $errorIndex = 0; my $snmpVersionBerUnit = constractBerData($INTEGER, changeDecNumToHexStr($snmpVersion)); my $commNameBerUnit = constractBerData($OCTET_STRING, unpack("H*", $commName)); my $requestIDBerUnit = constractBerData($INTEGER, changeDecNumToHexStr($requestID)); my $errorStatusBerUnit = constractBerData($INTEGER, changeDecNumToHexStr($errorStatus)); my $errorIndexBerUnit = constractBerData($INTEGER, changeDecNumToHexStr($errorIndex)); my $varbindData = constractVarbindData(); my $trapPduLength = getBinLengthFromHexStr( $requestIDBerUnit, $errorStatusBerUnit, $errorIndexBerUnit, $SEQUENCE, changeDecNumToHexStr(getBinLengthFromHexStr($varbindData)), $varbindData); my $totalength = getBinLengthFromHexStr( $snmpVersionBerUnit, $commNameBerUnit, $pduType, changeDecNumToHexStr($trapPduLength)) + $trapPduLength; my $trapData = $SEQUENCE . changeDecNumToHexStr($totalength) . $snmpVersionBerUnit . $commNameBerUnit . $pduType . changeDecNumToHexStr($trapPduLength) . $requestIDBerUnit . $errorStatusBerUnit . $errorIndexBerUnit . $SEQUENCE . changeDecNumToHexStr(getBinLengthFromHexStr($varbindData)) . $varbindData; return $trapData; } sub constractVarbindData { # ------------------------ # sysUpTime # ------------------------ my $sysUpTimeOid = '1.3.6.1.2.1.1.3.0'; my $sysUpTimeOidBerData = constractBerData($OBJECT_ID, convertOidStrToHexStr($sysUpTimeOid)); my $sysUpTime = 10; my $sysUpTimeBerData = constractBerData($TIME_TICKS, changeDecNumToHexStr($sysUpTime)); my $sysUpTimeVarbindLength = getBinLengthFromHexStr($sysUpTimeOidBerData, $sysUpTimeBerData); my $sysUpTimeVarbind = $SEQUENCE . changeDecNumToHexStr($sysUpTimeVarbindLength) . $sysUpTimeOidBerData . $sysUpTimeBerData; # ------------------------ # sysTrapOID # ------------------------ my $sysTrapOid = '1.3.6.1.6.3.1.1.4.1.0'; my $sysTrapOidBerData = constractBerData($OBJECT_ID, convertOidStrToHexStr($sysTrapOid)); my $sysTrapOidData = '1.3.6.1.6.3.1.1.5.1'; my $sysTrapBerData = constractBerData($OBJECT_ID, convertOidStrToHexStr($sysTrapOidData)); my $sysTrapVarbindLength = getBinLengthFromHexStr($sysTrapOidBerData, $sysTrapBerData); my $sysTrapVarbind = $SEQUENCE . changeDecNumToHexStr($sysTrapVarbindLength) . $sysTrapOidBerData . $sysTrapBerData; return $sysUpTimeVarbind . $sysTrapVarbind; } sub convertOidStrToHexStr { my $oidStr = shift; my @oids = split(/\./, $oidStr); my $hexStr = changeDecNumToHexStr(40*$oids[0] + $oids[1]); for (my $i = 2; $i <= $#oids; $i++) { $hexStr .= changeDecNumToHexStr($oids[$i]); } return $hexStr; } sub getBinLengthFromHexStr { my @hexStrs = @_; my $binLength = 0; foreach my $hexStr (@hexStrs) { $binLength += length($hexStr)/2; } return $binLength; } sub constractBerData { my ($tag, $data) = @_; $data =~ s/\s//g; my $binLength = (length($data))/2; my $berData = $tag . changeDecNumToHexStr($binLength) . $data; return $berData; } sub changeDecNumToHexStr { my $decNum = shift; my $hexStr = sprintf("%02x", $decNum); if (length($hexStr)%2 != 0) { $hexStr = '0' . $hexStr; } return $hexStr; }
上記のスクリプトが送信するtrapデータのパケットダンプは以下の通り。
30 40 02 01 01 04 06 70 75 62 6c 69 63 a7 33 02
01 0a 02 01 00 02 01 00 30 28 30 0d 06 08 2b 06
01 02 01 01 03 00 43 01 0a 30 17 06 0a 2b 06 01
06 03 01 01 04 01 00 06 09 2b 06 01 06 03 01 01
05 01
上記を簡単に解説すると以下の通り。
30 : SEQUENCE
40 : Total length
02 01 01 : SNMP version
04 06 70 75 62 6c 69 63 : community name
a7 : V2-TRAP-PDU
33 : PDU length
02 01 0a : request id (10)
02 01 00 : error status
02 01 00 : error index30 : SEQUENCE
28 : varbind list length30 : SEQUENCE
0d : varbind length
06 08 2b 06 01 02 01 01 03 00 : .1.3.6.1.2.1.1.3.0
43 01 0a : sysUpTime value (10)30 : SEQUENCE
17 : varbind length
06 0a 2b 06 01 06 03 01 01 04 01 00 : .1.3.6.1.6.3.1.1.4.1.0
06 09 2b 06 01 06 03 01 01 05 01 : .1.3.6.1.6.3.1.1.5.1
perl の Net::SNMP パッケージを使用すればもっと簡単に書けるが、SNMP trap のパケットの中身の勉強も兼ねて、一から作成してみた。
なお、今回作成したスクリプトについては、V1 trap にも対応したり、送信先などを引数で与えたりする等、今後いろいろエンハンスする予定。