Linuxのsnmptrapコマンドについて知る

## はじめに

[前回記事](https://qiita.com/t13801206/items/b7a1aaa8f87c5a72e1a1)までは、３つの記事に分けて、ZabbixとSNMPによる監視の勉強をしてきた。
よくわからないまま`snmptrap`コマンドを使っていたので、ここでいったん`snmptrap`に絞って勉強する。

#### ここまでの前提の話

DockerでUbuntuコンテナを立てて使ってきたので、今回も継続してUbuntuで勉強する。
（経緯については[前回記事](https://qiita.com/t13801206/items/b7a1aaa8f87c5a72e1a1)を参照）

`apt install snmp`により、`snmptrap`コマンドは使える状態。

また、UbuntuのIPアドレスは`172.20.240.7`であり、監視サーバのIPアドレスは`172.20.240.2`である。
SNMPトラップの送信先ポート番号は`1162`である。

:::note info
SNMPトラップの受信ポートは一般的には`162`である。
今回はZabbix公式が出しているDockerコンテナのパラメータに従って`1162`としている。
:::

## `snmptrap`コマンド

`snmptrap -h`を打つと、[こちらのサイトのURL](http://www.net-snmp.org/)が表示される。
　**注意**：リンク先はHTTPS非対応です

ここ数日はSNMPバージョン2を使ってきたので、[SNMPv2 Traps](http://www.net-snmp.org/wiki/index.php/TUT:snmptrap#SNMPv2_Traps)の項目を読む
以下、DeepLによる翻訳

>SNMPv2は通知リクエストのフォーマットを単純化し、Trapリクエストのためだけに別々のヘッダーフィールドを持つのではなく、すべてをvarbindリスト内に統合した。そのため、SNMPv2通知の最初の2つのvarbindは、sysUpTime.0に続いてsnmpTrapOID.0となる。この2番目のvarbindの値は、送信されるトラップを識別するOIDである。

よく分からない。とりあえず同じページに例示されているコマンドを打ってみる。
Ubuntuで試したところ、下記のコマンドが通った。

```bash
snmptrap -v 2c -c public 172.20.240.2:1162 ""  NET-SNMP-EXAMPLES-MIB::netSnmpExampleHeartbeatNotification SNMPv2-MIB::sysLocation.0 s "Just here"
```
トラップを受信したZabbixサーバのログでは、下記のように表示される。

```bash
UDP: [172.20.240.7]:47356->[172.20.240.2]:1162
DISMAN-EVENT-MIB::sysUpTimeInstance = 6251834
SNMPv2-MIB::snmpTrapOID.0 = NET-SNMP-EXAMPLES-MIB::netSnmpExampleHeartbeatNotification
SNMPv2-MIB::sysLocation.0 = Just here
```

1行目は送信元と送信先が`->`で表現されているだけだとして、2行目以降についてみていく。

### 一つ目の引数：`sysUpTime.0`

- `snmptrap`送信時は`""`で空白としていた。
- Zabbixサーバの受信ログによると、`6251834`の値が入っている。
- 実際の値を`snmpwalk`で取得した結果は`Timeticks: (1713670) 4:45:36.70`であった。

```bash
root@snmp-cl2:/usr/share/snmp/mibs# snmpwalk -v 2c -c public 172.20.240.7 DISMAN-EVENT-MIB::sysUpTimeInstance
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (1713670) 4:45:36.70
```
あらためて[SNMPv2 Traps](http://www.net-snmp.org/wiki/index.php/TUT:snmptrap#SNMPv2_Traps)の説明を読むと、下記の通り。

>snmptrapコマンドは、sysUpTime varbindには気の利いた値を挿入するので、実際には、トラップOID（およびOBJECTS句からの追加のvarbind）だけを提供すればよい

`sysUpTime`は空白にしておけば、受信側で適当に価を入れてくれるようなので、これ以上深追いはしない。
普段は、第一引数として`""`を書いておけばよい。それだけ。

### 二つ目の引数：`snmpTrapOID.0`

- 送信時は`NET-SNMP-EXAMPLES-MIB::netSnmpExampleHeartbeatNotification`とした。
- 受信側は`SNMPv2-MIB::snmpTrapOID.0 = NET-SNMP-EXAMPLES-MIB::netSnmpExampleHeartbeatNotification`となっている。
  
こちらは第一引数と違って、送信したものがそのまま受信側に反映されているので直感通りの動き。

なお、第二引数を省いて`snmptrap`を打った結果、「変数の型/値がありません」というエラーが出た。
もちろんサーバ側のログにも残らなかった。

```bash
root@snmp-cl2:/usr/share/snmp/mibs# snmptrap -v 2c -c public 172.20.240.2:1162 "" SNMPv2-MIB::sysLocation.0 s "Just here"
s: Missing type/value for variable
```

#### `snmpTrapOID.0`とは？

今回は`NET-SNMP-EXAMPLES-MIB::netSnmpExampleHeartbeatNotification`
としたが、前回まではよく分からないまま`coldstart`としていた。

試しに`snmptrap -v 2c -c public 172.20.240.2:1162 "" "test" SNMPv2-MIB::sysLocation.0 s "thisistest"`を送ってみる。
`snmpTrapOID.0`の箇所を`"test"`としてみた。

すると、受信側のサーバのログでは`IF-MIB::ifTestGroup`という文字列になっていた。

```bash
UDP: [172.20.240.7]:51141->[172.20.240.2]:1162
DISMAN-EVENT-MIB::sysUpTimeInstance = 6514663
SNMPv2-MIB::snmpTrapOID.0 = IF-MIB::ifTestGroup
SNMPv2-MIB::sysLocation.0 = thisistest
```

`coldstart`とした場合は`SNMPv2-MIB::coldStart`という値になる。

```bash
UDP: [172.20.240.7]:40061->[172.20.240.2]:1162
DISMAN-EVENT-MIB::sysUpTimeInstance = 6591700
SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-MIB::coldStart
SNMPv2-MIB::sysLocation.0 = america
```

`"a"`にすると`SNMPv2-MIB::authenticationFailure`になった。

```bash
UDP: [172.20.240.7]:41152->[172.20.240.2]:1162
DISMAN-EVENT-MIB::sysUpTimeInstance = 6673876
SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-MIB::authenticationFailure
SNMPv2-MIB::sysLocation.0 = japan
```

[RFC 3416](https://tex2e.github.io/rfc-translater/html/rfc3416.html)「4.2.6. SNMPv2-Trap-PDU」と[RFC 3418](https://tex2e.github.io/rfc-translater/html/rfc3418.html)に「よく知られたトラップ」が書かれている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/8a1e23bf-5cad-37b9-4481-6b298873bba4.png)

つまりは、トラップを発出した時点で、そのホストが起動中だったのか、起動した直後だったのか、インターフェイスがUP/DOWNだったのかを知らせるものなのかもしれない。
[こちら富士通の記事](https://software.fujitsu.com/jp/manual/manualfiles/M050006/J2X11430/04Z200/dmrefad/dmref440.html)から引用させていただく
>cold start
SNMPエージェントが初期化され起動しました。
warm start
SNMPエージェントが初期化されず(オブジェクト状態は変更なく)再起動しました。
link down
ネットワークとのインタフェースがUPからDOWN状態になりました。
link up
ネットワークとのインタフェースがDOWNからUP状態になりました。

このトラップがテストであることを明示するときは、`NET-SNMP-EXAMPLES-MIB::netSnmpExampleHeartbeatNotification`を使うのだろうか。

コメントでご指摘いただけると幸いです。

### 三つ目の引数は自由（トラップしたいOID）

`SNMPv2-MIB::sysLocation.0 s "Just here"`の意味は
`sysLocation.0`というOIDの値を「Just here」にしてトラップ送信するという意味

`s`は文字列の`string`の意味
ここを`i`(整数を意味する integer)にすると、`SNMPv2-MIB::sysLocation.0: Bad variable type (Type of attribute is OCTET STRING, not INTEGER)`というエラーが吐かれる

変更したい値の型は`snmpwalk`で調べられる
たとえば次の１行目は整数Integerで、2行目は文字列Stringの型
```bash
HOST-RESOURCES-MIB::hrSWRunIndex.235 = INTEGER: 235
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "bash"
```

[こちら](https://net-snmp.sourceforge.io/tutorial/tutorial-5/commands/snmpset.html)に型一覧が載っている。

## おわりに

`snmptrap`の各引数が何者であるかはわかったので、次回以降はテキトーにコマンドを打つことはなくなると思う。
しかしながら、`snmpTrapOID.0`に設定すべき値についてはまだ理解が浅いので、継続して勉強する。

さしあたっては、第三引数以降が大事（自分がトラップ送信したい値）であることがよくわかった。

### おわりの余談

第三引数と第四引数のOIDを同一にして送ってみた。

`snmptrap -v 2c -c public 172.20.240.2:1162 '' "a" SNMPv2-MIB::sysLocation.0 s 'japan'  SNMPv2-MIB::sysLocation.0 s 'japan2'`

Zabbixサーバのログでは、両方の値が表示されていた。
```bash
UDP: [172.20.240.7]:57826->[172.20.240.2]:1162
DISMAN-EVENT-MIB::sysUpTimeInstance = 6799884
SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-MIB::authenticationFailure
SNMPv2-MIB::sysLocation.0 = japan
SNMPv2-MIB::sysLocation.0 = japan2
```

ZabbixのGUIでもこのとおり

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/0c21b5a4-a653-c4bf-c3a9-a9988fc1bfe8.png)



## 参考

https://qiita.com/todanano/items/fd9a9ab20e3f3ec65bee