SNMPTRAPの文字化け回避策（Zabbixにも使える)

# やりたいこと

日本語版vCenterやWindowsからの日本語トラップが文字化け(↓)することの対処。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/82244/09f6c1c1-ba74-177f-c8e7-3c05eabfc723.png)

こんな感じ(↓)にしたい！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/82244/54cb8a8b-b030-6add-9de9-19670bda56ff.png)

# 環境
snmptrapdとsnmpttを使用したzabbix環境

* SNMPTT v1.4beta2
* NET-SNMP Version:  5.7.2
* Zabbix 3.0.9

# 実現方法

標準で構築すると、
snmptrapdはsnmptt用ハンドラ(/usr/sbin/snmptthandler)を直接呼ぶ。

が、今回は、
snmptt用ハンドラを呼ぶ前に、16進数テキストをバイナリに変換する。


# 構築するにあたり挙動の確認
## snmptrapd が吐き出すもの（サンプル)

```ruby:UTF8(vCenterやLinux系)のアラーム(あくまでもサンプル)
1497051259
hostname
UDP: [192.168.10.84]:48146->[192.168.10.84]:162
.1.3.6.1.2.1.1.3.0 0:1:24:40.47
.1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.3.1.1.0.99
.1.3.6.1.4.1.8072.9999.1 "31 32 33 E6 97 A5 E6 9C AC E8 AA 9E "
.1.3.6.1.4.1.8072.9999.2 "E6 97 A5 E6 9C AC E8 AA 9E 34 35 36 E3 81 82 E3 
81 82 E3 81 82 "
.1.3.6.1.4.1.8072.9999.3 "E5 AF BF E9 99 90 E7 84 A1 E3 80 81 E5 AF BF E9 
99 90 E7 84 A1 E4 BA 94 E5 8A AB E3 81 AE E6 93 
A6 E3 82 8A E5 88 87 E3 82 8C E6 B5 B7 E7 A0 82 
E5 88 A9 E6 B0 B4 E9 AD 9A E3 81 AE E6 B0 B4 E8 
A1 8C E6 9C AB E9 9B B2 E6 9D A5 E6 9C AB E9 A2 
A8 E6 9D A5 E6 9C AB E9 A3 9F E3 81 86 E5 AF 9D 
E3 82 8B E5 87 A6 E3 81 AB E4 BD 8F E3 82 80 E5 
87 A6 E8 97 AA E3 82 89 E6 9F 91 E5 AD 90 E3 81 
AE E8 97 AA E6 9F 91 E5 AD 90 E3 83 91 E3 82 A4 
E3 83 9D E3 83 91 E3 82 A4 E3 83 9D E3 83 91 E3 
82 A4 E3 83 9D E3 81 AE E3 82 B7 E3 83 A5 E3 83 
BC E3 83 AA E3 83 B3 E3 82 AC E3 83 B3 E3 82 B7 
E3 83 A5 E3 83 BC E3 83 AA E3 83 B3 E3 82 AC E3 
83 B3 E3 81 AE E3 82 B0 E3 83 BC E3 83 AA E3 83 
B3 E3 83 80 E3 82 A4 E3 82 B0 E3 83 BC E3 83 AA 
E3 83 B3 E3 83 80 E3 82 A4 E3 81 AE E3 83 9D E3 
83 B3 E3 83 9D E3 82 B3 E3 83 94 E3 83 BC E3 81 
AE E3 83 9D E3 83 B3 E3 83 9D E3 82 B3 E3 83 8A 
E3 83 BC E3 81 AE E9 95 B7 E4 B9 85 E5 91 BD E3 
81 AE E9 95 B7 E5 8A A9 "
.1.3.6.1.6.3.18.1.3.0 192.168.10.84
.1.3.6.1.6.3.18.1.4.0 "public"
.1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.3.1.1
```

```ruby:CP932(Windowsフォーマット)のアラーム(あくまでもサンプル)
1497027708
hostname
UDP: [192.168.10.35]:60869->[192.168.10.84]:162
.1.3.6.1.2.1.1.3.0 0:0:51:24.96
.1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.311.1.13.1.9.84.101.115.116.98.97.116.99.104.0.1
.1.3.6.1.4.1.311.1.13.1.9999.1.0 "83 70 83 89 83 81 81 5B 83 5E 81 5B 20 27 4D 65 
73 73 61 67 65 20 82 CC 88 F8 90 94 82 F0 8A 6D 
94 46 82 C5 82 AB 82 DC 82 B9 82 F1 81 42 88 F8 
90 94 82 AA 20 6E 75 6C 6C 0D 0A "
.1.3.6.1.4.1.311.1.13.1.9999.2.0 "Unknown"
.1.3.6.1.4.1.311.1.13.1.9999.3.0 "win2016"
.1.3.6.1.4.1.311.1.13.1.9999.4.0 "1"
.1.3.6.1.4.1.311.1.13.1.9999.5.0 "1"
.1.3.6.1.4.1.311.1.13.1.9999.6.0 "83 70 83 89 83 81 81 5B 83 5E 81 5B 20 27 4D 65 
73 73 61 67 65 20 82 CC 88 F8 90 94 82 F0 8A 6D 
94 46 82 C5 82 AB 82 DC 82 B9 82 F1 81 42 88 F8 
90 94 82 AA 20 6E 75 6C 6C "
.1.3.6.1.6.3.18.1.3.0 192.168.10.35
.1.3.6.1.6.3.18.1.4.0 "public"
.1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.311.1.13.1.9.84.101.115.116.98.97.116.99.104
```

## snmptrapd が吐き出すもの(考察)

* 最初の数行に、時刻・ホスト・パケット(src/dest)等の情報がある(今回は変換対象外)
* 各OIDと値(文字列や数字)がペアで格納している
* 上記ペアは基本１行であるが、値が長くなると複数行に跨る
* 跨った値は、始点終点がわかるようにダブルクォート(")で囲まれている
* マルチバイトの場合、"２桁の１６進数＋空白"のパターンの繰り返しである
* 上記１６進数をバイナリとしてつなげるとマルチバイト文字列となる

## やること(詳細)

値部分のダブルクォート(")で囲まれた文字列が、パターン(２桁１６進数+空白)であれば、マルチバイトとみなし、バイナリデータ(マルチバイト文字)に変換する
その他の文字列は触らない。また、ダブルクォートで囲まれていても、パターンに一致しなければ変換しない

上記やることを実現したプログラムをgithubに登録した
* https://github.com/mishikawan/conv_snmptrap_mb.git


# 実際の手順
1. snmptrapdが稼働しているサーバに、プログラムを１本置く。
2. snmptrapd.confを修正する。
3. 以上！！

### プログラムを取ってくる

```bash
$ git clone https://github.com/mishikawan/conv_snmptrap_mb.git
$ cp -p conv_snmptrap_mb/conv_snmptrap_mb /usr/local/bin/.
$ chmod a+x /usr/local/bin/conv_snmptrap_mb
```

### snmptrapd.confの設定(その１(望ましい)）
以下のように設定し、snmptrapdのrestartする。
マルチバイトのトラップのOIDがわかっている場合は、こちらが望ましい。

e.g.) 以下のような仕様の場合
1. vCenter(1.3.6.1.4.1.6876.*) ... UTF8
2. windows-event(1.3.6.1.4.1.311.*) ...CP932
3. それ以外は、シングルバイトのトラップ

```ruby:/etc/snmp/snmptrapd.conf
disableAuthorization yes

traphandle 1.3.6.1.4.1.6876.* /usr/local/bin/conv_snmptrap_mb | /usr/sbin/snmptthandler
traphandle 1.3.6.1.4.1.311.* /usr/local/bin/conv_snmptrap_mb | iconv -f cp932 | /usr/sbin/snmptthandler
traphandle default /usr/sbin/snmptthandler
```
* UTF8の場合は別途iconvいらない( 下から３行目 )
* CP932(UTF8以外のエンコード)の場合は、iconvの別途記載が必要( 下から２行目 )

### snmptrapd.confの設定(その２(楽ちんだけど非推奨)）
以下のように設定し、snmptrapdのrestartする。
これで、vCenterやUTF8で飛んでくるトラップにも対応できる。
けどこれじゃあ、CP932の場合は文字化けするし、すべてのトラップが私のプログラムを通過することで怖い。

```ruby:/etc/snmp/snmptrapd.conf
disableAuthorization yes

traphandle default /usr/local/bin/conv_snmptrap_mb | /usr/sbin/snmptthandler
```

# 最後に

明確な回避策がインターネット上になく、困ったため作りました。

プログラム組める人じゃないのでコーディングセンス無いですが、私の環境では問題なく動いています。
本当は、Encode::Guess も使いたかったですが、trapのOID毎に使用するエンコードは人が判別できるため、設定で回避しています。