技術メモ

神奈川在住のITエンジニアの備忘録。おもにプログラミングやネットワーク技術について、学んだことを自分の中で整理するためにゆるゆると書いています。ちゃんと検証できていない部分もあるのでご参考程度となりますが、誰かのお役に立てれば幸いです。

SNMP v2c trap (coldStart) 送信スクリプト

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 index

30 : SEQUENCE
28 : varbind list length

30 : 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 にも対応したり、送信先などを引数で与えたりする等、今後いろいろエンハンスする予定。