net-snmpと、PINGとPONGをやり取りする仲になる


net-snmpのpass_persistを使って、
自作したsnmp agentで応答を返す。


以下自作agent。

```cpp:a.cpp

#include <iostream>
#include <string>

void getnext_request( std::string &oid ) {

        if(
                oid == ".1.3.6.1.4.1.99999" ||
                oid == ".1.3.6.1.4.1.99999.1"
        ) {

                std::cout
                        << ".1.3.6.1.4.1.99999.1.0" << std::endl
                        << "integer" << std::endl
                        << 42 << std::endl
                ;

        } else {

                // 該当する内容が無い場合、NONE\nを返す
                std::cout << "NONE" << std::endl;

        }

}

void get_request( std::string &oid ) {

        if( oid == ".1.3.6.1.4.1.99999.1.0" ) {

                std::cout
                        << oid << std::endl
                        << "integer" << std::endl
                        << 42 << std::endl
                ;

        } else {

                // 該当する内容が無い場合、NONE\nを返す
                std::cout << "NONE" << std::endl;

        }

}

int main( int argc, char **argv ) {

        while( 1 ) {

                std::string line;
                std::cin >> line;

                if( line.empty() ) {

                        break;

                } else if( line == "PING" ) {

                        std::cout << "PONG" << std::endl;

                } else if( line == "get" ) {

                        std::string oid;
                        std::cin >> oid;

                        get_request( oid );

                } else if( line == "getnext" ) {

                        std::string oid;
                        std::cin >> oid;

                        getnext_request( oid );

                } else {

                        // unk.
                        break;

                }

        }

}

```

このサンプルでは、getで.1.3.6.1.4.1.99999.1.0しか応答しない。
もしこれを元にagentを作成する場合は、
getnext_requestとget_requestの部分を、相当作りこむ必要がある。



上記ソースをコンパイルする。
実行ファイルは/root/a.outとする。


snmpd.confを以下のようにする。

<pre>

#
syslocation     あなたの心の中
syscontact      todanano

#               sec.name        source          community
com2sec         Net-Local       0.0.0.0         public

#               groupName       securityModel   securityName
group           Net-Group       v1              Net-Local
group           Net-Group       v2c             Net-Local

#               name            incl/excl       subtree         mask(optional)
view            all             included        .1

#               group           context sec.model       sec.level       prefix  read    write   notif
access          Net-Group       ""      any             noauth          exact   all     none    none
access          Net-Group       ""      any             priv            exact   all     none    none

pass_persist .1.3.6.1.4.1.99999 /root/a.out

</pre>

pass_persist .1.3.6.1.4.1.99999 /root/a.outの部分が、
作成した自作エージェントを設定している箇所。
.1.3.6.1.4.1.99999以下のリクエストが来た場合、
/root/a.outに渡すようになる。

それ以外は、色々書いてあるが、最低限、応答が返せる内容であれば
何でも良い。

snmpd.confを書き換えたら、snmpdを再起動する。

<pre>
[root@localhost ~]# /etc/init.d/snmpd restart
Stopping snmpd:                                            [  OK  ]
Starting snmpd:                                            [  OK  ]
[root@localhost ~]#
</pre>


snmpgetかsnmpwalkを使って、.1.3.6.1.4.1.99999.1.0が応答することを確認する。
※snmpgetの場合、oidを間違えないこと。間違えた場合、エラーとなる。

<pre>
[root@localhost ~]# snmpget -v 1 -c public localhost .1.3.6.1.4.1.99999.1.0
SNMPv2-SMI::enterprises.99999.1.0 = INTEGER: 42
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.99999
SNMPv2-SMI::enterprises.99999.1.0 = INTEGER: 42
[root@localhost ~]#
[root@localhost ~]# snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.99999.1
SNMPv2-SMI::enterprises.99999.1.0 = INTEGER: 42
[root@localhost ~]#
[root@localhost ~]# snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.99999.1.0
SNMPv2-SMI::enterprises.99999.1.0 = INTEGER: 42
[root@localhost ~]#
[root@localhost ~]#
</pre>


先にも書いたが、上記サンプルでは、.1.3.6.1.4.1.99999.1.0しか応答を返せないので、
もしこれ以外の内容を返したい場合は、作りこむ必要がある。
（特にgetnextの内容は、相当考える必要がある。矛盾無く、次のoidを返せる仕組みが必要）

また、pass_persistによって呼び出されるプロセスは、
一度実行された後は、起動し続ける。
これは、snmpwalkなど、連続で問い合わせを受ける場合、
応答速度の面で恩恵がある。
また、前回値と今回値の差分値を返す場合、
プロセスが起動し続けているため、
シンプルな実装が可能となる（一時ファイル等を使う必要が無い）。

該当するoidにリクエストが来たタイミングで、対象のプロセスが動作していない場合、
snmpdはプロセスの実行を試みる。
すでに動作している場合は、そのままやり取りをする。

一度起動したプロセスは、snmpdの停止（再起動）によって、停止する。

<pre>
[root@localhost ~]# /etc/init.d/snmpd restart
Stopping snmpd:                                            [  OK  ]
Starting snmpd:                                            [  OK  ]
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# ps aux | grep a.out
root     15833  0.0  0.0  61176   748 pts/0    S+   10:09   0:00 grep a.out
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.99999.1.0
SNMPv2-SMI::enterprises.99999.1.0 = INTEGER: 42
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# ps aux | grep a.out
root     15839  0.0  0.0  11564  1008 ?        S    10:09   0:00 /root/a.out
root     15843  0.0  0.0  61176   748 pts/0    S+   10:09   0:00 grep a.out
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# /etc/init.d/snmpd restart
Stopping snmpd:                                            [  OK  ]
Starting snmpd:                                            [  OK  ]
[root@localhost ~]#
[root@localhost ~]# ps aux | grep a.out
root     15865  0.0  0.0  61176   748 pts/0    S+   10:09   0:00 grep a.out
[root@localhost ~]#
[root@localhost ~]#
</pre>

仮に指定したプロセスが、起動し続けるような実装になっていない場合でも、
単にいなければ実行してからやり取りをするだけなので、とりあえず動作する。

ただ、起動し続けるプログラムよりも、オーバーヘッドが大きくなるため、
pass_persistで設定する意味は無い。

実行したら終了したい場合は、pass_persistではなく、passを使ったほうが良い。
passであれば、PING、PONGのやり取りをせずに、そのままリクエストを受け取れるため、
実装もシンプルになるだろう。