snmp trapをircで見る

何だかよくわからない

スイッチのcpu使用率は急に落ち着いてきました.何もしてないんだけどなぁ.不気味です.だからって監視をやめてしまうと後が怖いので,継続してsyslogを見る事にしました.そんなとき普通は,syslogサーバにスイッチのlogを飛ばして/var/log/syslogを定期的に眺めるもんだと思いましたが,すぐに飽きてしまいそうなので,logをircに飛ばしてみることにしました.

CISCO-SYSLOG-MIB

最初はsyslogファイルをtailして,差分をircに投げようかと思ったのですが,なんかアーキテクチャが気持ち悪い.専用のsyslogdを作ることもちょっと考えたんですが,偶然snmp trapでsyslogメッセージを送れる事に気がつきました.実際に

snmp-server host loghost version 2c communityString syslog

なんて設定をスイッチに入れてケーブルの抜き差しなんてことをやってみると,syslogと同じメッセージが飛んでいます.そこで簡単なtrapdを作る事にしました.Net::SNMPとかCPANにあるSNMP関連のモジュールではtrapdを簡単に作るサンプルがないんですが,mrtgで使われているhttp://www.switch.ch/misc/leinen/snmp/perl/にはtrapdのサンプルが着いています.これを参考にすることにしました.ちなみに,このライブラリはModule::ThirdParty - Provide information for 3rd party modules (outside CPAN) - metacpan.orgでも紹介されている有名どころなので,覚えておいて損はないと思います.

syslog-trap.pl

ってことで作ってみたスクリプトは以下のようになります.

#!/usr/local/bin/perl
use strict;
use warnings;
use lib 'SNMP_Session-1.10/lib';
use SNMP_Session;
use BER;
use Socket;
use POE::Component::IKC::ClientLite;
use Term::ANSIColor qw(:constants);

sub msg (@) { print GREEN, BOLD, " * ", RESET, "@_\n" }
my ( %status, $session, $trap, $sender, $sender_port );

%status = (
    1 => 'EMERGENCY',
    2 => 'ALERT',
    3 => 'CRITICAL',
    4 => 'ERROR',
    5 => 'WARNING',
    6 => 'NOTICE',
    7 => 'INFO',
    8 => 'DEBUG',
);

$session = SNMPv2c_Session->open_trap_session()
  or die "cannot open trap session";

msg "trapd start";

while ( ( $trap, $sender, $sender_port ) = $session->receive_trap() ) {
    print_trap( $sender, $session, $trap );
}

sub print_trap {
    my ( $sender, $session, $trap ) = @_;
    my ( $binding, $severity, $message );
    my ( $community, $ent, $agent, $gen, $spec, $dt, $bindings ) =
      $session->decode_trap_request($trap);
    while ($bindings) {
        my ( $oid, $value );
        ( $binding, $bindings ) = decode_sequence($bindings);
        ( $oid, $value ) = decode_by_template( $binding, "%O%@" );

        $severity = pretty_print($value)
          if ( BER::pretty_oid($oid) =~ /1.3.6.1.4.1.9.9.41.1.2.3.1.3/ );
        $message = pretty_print($value)
          if ( BER::pretty_oid($oid) =~ /1.3.6.1.4.1.9.9.41.1.2.3.1.5/ );
    }
    if ( $severity && $message ) {
        my $notify = sprintf "%s - %s %s on %s", $status{$severity},
          inet_ntoa($sender), $message, scalar localtime(time);

        msg($notify);
        if ( $severity < 4 ) {
            my $r = POE::Component::IKC::ClientLite::create_ikc_client(
                port    => 9999,
                ip      => "localhost",
                name    => "syslogtrap$$",
                timeout => 5,
            ) || die "create_ikc\n";
            $r->post( 'notify_irc/update', $notify );
        }
    }
}

昨日の日記に書いたirclient.plを拡張しています.サーバ側は同じnotify-irc.plスクリプトです.スイッチから何かログがとんで来たらとりあえずコンソールにメッセージを出力し,CRITICAL/ALERT/EMERGENCYレベルのログであればircに飛ばします.SNMPのオブジェクト定義はftp://ftp.cisco.com/pub/mibs/v2/CISCO-SYSLOG-MIB.myにあったものを参考にしました.ログの出力イメージはこんな感じになります.

* ERROR - 10.0.0.1 Interface FastEthernet4/0/15, changed state to up on Wed Oct 10 14:39:09 2007
* ERROR - 10.0.0.1 Interface FastEthernet4/0/15, changed state to down on Wed Oct 10 14:40:25 2007

なぜ,ポートのアップダウンごときがエラーになるのだ?!とかそんな感想はさておきなんとか適当に動いているようです.

さて,

とりあえず,撒き餌はしました.後は魚が網にかかるのを待つだけです.どんな魚がいるのか,そしてそれをめでたく釣り上げることはできるのか.しばらくは様子見です.