以前作成した以下の perl スクリプトをエンハンスした。
akrad.hatenablog.com
具体的には、SNMPv1 のcoldStartトラップも SNMPv2cのcoldStartトラップも送信できるようにし、トラップ送信先やSNMPv1トラップの agent-addrフィールドも指定できるようにした。
使い方の例は以下。
1.1.1.1 に、SNMPv2cのcoldStartトラップを送信する。
perl sendTrap.pl -v 2 -d 1.1.1.1
1.1.1.1 に、agent-addrフィールドを 2.2.2.2 として、SNMPv1のcoldStartトラップを送信する。
perl sendTrap.pl -v 1 -d 1.1.1.1 -a 2.2.2.2
作成したperlスクリプトは以下の通り。
sendTrap.pl
use strict; use warnings; use Socket; use Getopt::Long 'GetOptions'; #--------------- # Constant value #--------------- my $V1_TRAP = 'a4'; my $V2c_TRAP = 'a7'; my $SNMP_V1 = 0; my $SNMP_V2C = 1; my $INTEGER = '02'; my $OCTET_STRING = '04'; my $OBJECT_ID = '06'; my $SEQUENCE = '30'; my $IP_ADDRESS = '40'; my $TIME_TICKS = '43'; #------------------- # Set default value #------------------- my $destAddr = 'localhost'; my $destPort = 162; my $snmpVersion = '2'; my $enterpriseOid = '1.2.3.4.5'; my $agentAddr = '127.0.0.1'; GetOptions( 'dest=s' => \$destAddr, 'version=s' => \$snmpVersion, 'agentaddr=s' => \$agentAddr ); #------------------- # Check user's input #------------------- if ($destAddr =~ /^\d/) { # This is an ip address. Because hostname doesn't start from a digit. if (!isIpAddress($agentAddr)) { die("$destAddr is not an ip address."); } } if (($snmpVersion ne '1') and ($snmpVersion ne '2') and ($snmpVersion ne '2c')) { die("$snmpVersion must be 1 or 2(2c)"); } if (!isIpAddress($agentAddr)) { die("$agentAddr is not an ip address."); } #--------------- # MAIN #--------------- my $socket; socket($socket, PF_INET, SOCK_DGRAM, 0) or die "Failed to create socket. $!"; my $addrBin = inet_aton($destAddr); my $sockAddr = pack_sockaddr_in($destPort, $addrBin); my $trapData; if ($snmpVersion eq '1') { $trapData = constractV1TrapData(); } elsif (($snmpVersion eq '2') or ($snmpVersion eq '2c')) { $trapData = constractV2cTrapData(); } else { die("Need to specify 1 or 2(2c) for version."); } my $trapDataBin = pack("H*", $trapData); send($socket, $trapDataBin, 0, $sockAddr) or die "Failed to send. $!"; #--------------- # Sub functions #--------------- sub constractV1TrapData { my $snmpVersion = $SNMP_V1; my $commName = 'public'; my $pduType = $V1_TRAP; my $requestID = 10; my $errorStatus = 0; my $errorIndex = 0; my $genericTrap = 0; my $specificTrap = 0; my $timeStamp = 0; my $snmpVersionBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($snmpVersion)); my $commNameBerUnit = constractBerData($OCTET_STRING, unpack("H*", $commName)); my $enterpriseOidBerUnit = constractBerData($OBJECT_ID, convertOidStrToHexStr($enterpriseOid)); my $agentAddrBerUnit = constractBerData($IP_ADDRESS, convertIpAddrStrToHexStr($agentAddr)); my $genericTrapBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($genericTrap)); my $specificTrapBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($specificTrap)); my $timeStampBerUnit = constractBerData($TIME_TICKS, convertDecNumToHexStr($timeStamp)); my $emptyVarbindBerUnit = constractBerData($SEQUENCE, ""); my $trapPduLength = getBinLengthFromHexStr( $enterpriseOidBerUnit, $agentAddrBerUnit, $genericTrapBerUnit, $specificTrapBerUnit, $timeStampBerUnit, $emptyVarbindBerUnit); my $totalength = getBinLengthFromHexStr( $snmpVersionBerUnit, $commNameBerUnit, $pduType, convertDecNumToHexStr($trapPduLength)) + $trapPduLength; my $trapData = $SEQUENCE . convertDecNumToHexStr($totalength) . $snmpVersionBerUnit . $commNameBerUnit . $pduType . convertDecNumToHexStr($trapPduLength) . $enterpriseOidBerUnit . $agentAddrBerUnit . $genericTrapBerUnit . $specificTrapBerUnit . $timeStampBerUnit . $emptyVarbindBerUnit; return $trapData; } sub constractV2cTrapData { my $snmpVersion = $SNMP_V2C; my $commName = 'public'; my $pduType = $V2c_TRAP; my $requestID = 10; my $errorStatus = 0; my $errorIndex = 0; my $snmpVersionBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($snmpVersion)); my $commNameBerUnit = constractBerData($OCTET_STRING, unpack("H*", $commName)); my $requestIDBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($requestID)); my $errorStatusBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($errorStatus)); my $errorIndexBerUnit = constractBerData($INTEGER, convertDecNumToHexStr($errorIndex)); my $varbindData = constractVarbindData(); my $trapPduLength = getBinLengthFromHexStr( $requestIDBerUnit, $errorStatusBerUnit, $errorIndexBerUnit, $SEQUENCE, convertDecNumToHexStr(getBinLengthFromHexStr($varbindData)), $varbindData); my $totalength = getBinLengthFromHexStr( $snmpVersionBerUnit, $commNameBerUnit, $pduType, convertDecNumToHexStr($trapPduLength)) + $trapPduLength; my $trapData = $SEQUENCE . convertDecNumToHexStr($totalength) . $snmpVersionBerUnit . $commNameBerUnit . $pduType . convertDecNumToHexStr($trapPduLength) . $requestIDBerUnit . $errorStatusBerUnit . $errorIndexBerUnit . $SEQUENCE . convertDecNumToHexStr(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, convertDecNumToHexStr($sysUpTime)); my $sysUpTimeVarbindLength = getBinLengthFromHexStr($sysUpTimeOidBerData, $sysUpTimeBerData); my $sysUpTimeVarbind = $SEQUENCE . convertDecNumToHexStr($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 . convertDecNumToHexStr($sysTrapVarbindLength) . $sysTrapOidBerData . $sysTrapBerData; return $sysUpTimeVarbind . $sysTrapVarbind; } sub isIpAddress { my $ipAddr = shift; my @octets = split(/\./, $ipAddr); return 0 if (@octets != 4); foreach my $octet (@octets) { if (!($octet =~ /^\d+$/) or !(0 <= $octet and $octet <= 255)) { return 0; } } return 1; } sub convertIpAddrStrToHexStr { my $ipAddrStr = shift; my @octets = split(/\./, $ipAddrStr); my $ipAddrHexStr; foreach my $octet (@octets) { $ipAddrHexStr .= convertDecNumToHexStr($octet); } return $ipAddrHexStr; } sub convertOidStrToHexStr { my $oidStr = shift; my @oids = split(/\./, $oidStr); my $hexStr = convertDecNumToHexStr(40*$oids[0] + $oids[1]); for (my $i = 2; $i <= $#oids; $i++) { if ($oids[$i] < 128) { $hexStr .= convertDecNumToHexStr($oids[$i]); } else { $hexStr .= convertOidNumToHexStrForMultiBytes($oids[$i]); } } return $hexStr; } sub convertOidNumToHexStrForMultiBytes { my $oidNum = shift; my $hexStr = ""; my $counter = 0; my $workVal = $oidNum; while (1) { my $quotient = int($workVal / 128); my $remainder = $workVal % 128; if ($counter != 0) { $remainder += 128; } $hexStr = convertDecNumToHexStr($remainder) . $hexStr; if ($quotient == 0) { last; } $workVal = $quotient; $counter++; } 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 . convertDecNumToHexStr($binLength) . $data; return $berData; } sub convertDecNumToHexStr { my $decNum = shift; my $hexStr = sprintf("%02x", $decNum); if (length($hexStr)%2 != 0) { $hexStr = '0' . $hexStr; } return $hexStr; }
上記のスクリプト作成に当たっては、OID を BER(Basic Encoding Rules) 形式でエンコーディングするのがちょっと面倒だった。(convertOidStrToHexStr関数やconvertOidNumToHexStrForMultiBytes関数の辺り・・)
この辺の話については、別途、記事を書きたいと思っている。