SNMPでWIFIルーターの電波OFFをする

## やりたいこと
WIFIルーターの電波on/offをリモートで行う。

## 前提となる知識
### SNMP(Simple Network Management Protocol)
- ネットワーク機器を管理するために用いられるプロトコル。今回でいうと、ルーターの設定値を読み出したり、電波をon/offしたり。
- ルーターの設定をSNMPプロトコルを通じて変更が可能 (変更が許可されているもののみ)。
- ルーターの設定値を読み出すことも可能。

### MIBファイル形式

- MIBファイルにはルーターの各設定について、OID(Object ID)が何で、データ型が何なのか、設定許可されているかどうかなどが記載されている。
- ルーターの各設定項目について、それぞれObject ID(OID)が設定されている(メーカー独自)ため、ルーターのメーカーからMIBファイルを入手する必要がある。もしくはルーターの管理画面からダウンロードできるのかもしれない。
- MIBファイル形式について
- [Q:SNMPv2形式の拡張MIBファイルの基本的な文法](https://software.fujitsu.com/jp/manual/manualfiles/M090045/J2X12071/05Z200/J2071-00-07-10-00.html)

### OID(Object ID)について
設定値がツリー構造になっており、数字を順番に辿っていくと、ある設定値にたどり着く。たとえば、以下の例だとxxはベンダー名が割り当てられている。それ以降はモデル名だったり、設定値だったりと続いていく。これはベンダー毎に定義がされている。

```
1.3.6.1.4.1.xx
```

- [1.3.6.1.4.1 - IANA-registered Private Enterprises](https://www.alvestrand.no/objectid/1.3.6.1.4.1.html)

実際のMIBファイルはテキストになっているが、解読するのは結構大変だった。以下は実際の例。

- [2.6.11 拡張MIBオブジェクトの定義例](http://itdoc.hitachi.co.jp/manuals/3020/30203T0401/SNMP0050.HTM)

## 使用したツール
### TWSNMP
http://www.twise.co.jp/twsnmp.html

MIBファイルを読み込ませる必要がある。GUIで、設定値が一覧で見えてくるので直感的でわかりやすい。現在設定値の確認、ObjectIDの確認などを行うときに使用した。

### snmpwalk (windowns)
コマンドラインで設定の読み出しや変更を行う。実際にプログラム内でコマンドを投げるときに、正しくObjectIDや型などを送信しないといけないのだが、そのコマンドの確認のために使用。TWSNMPだと、実際にどういうコマンドが投げられているのかはわからない。Object IDはわかるのだが、型をどう指定するのかとか、Object IDの末尾の数字をどうするとか、そのあたりが確認できた。

例えば、1.3.6.1.4.1.xx.xx.xx.xxというObject IDの値を読み出したいというとき、SNMPv2-SMI::なんとかと返ってくる。ちなみに-vはSNMPのバージョン(今回はv2)。 -cはコミュニティ名。ここでは"public"という名前(ルーターの設定でsnmp communityという項目があるはず)。1.3.6.1.4.1.xx.xx.xx.xxのあとに、1とか0などがでてきて、データ型がなんなのかを教えてくれる。で、実際にコマンドを投げるときは、1.3.6.1.4.1.xx.xx.xx.xx.1をObjectIDとする必要があった。ここがわからず結構はまった。

```:設定値の読み出し
c:\usr\bin>snmpwalk -v 2c -c public 192.168.1.10 1.3.6.1.4.1.xx.xx.xx.xx
SNMPv2-SMI::enterprises.xx.xx.xx.xx.1 = INTEGER:0
```

- 設定の書き込みをする場合はルーターのsnmpの設定で、READ-WRITEの設定をしておく(書き込み可能にしておく)。
- enterprises.xx.xx.xx.xx.1の最後に1なのか0なのかはMIBファイルを見ただけではわからなかった。(何か規則性がある？)
- ここで得られたenterprises.xx.xx.xx.xx.1をOIDとして設定している。 

### SharpSnmp (.NET SNMP Library)
SNMPを行うためのライブラリ。いくつか似たようなのがある。

[SNMP Library Documentation](https://docs.sharpsnmp.com/)
インストール方法はここに書いてある。

```c#
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
```

- SNMPv2を使用した。v2はわりとシンプル。v3は認証などもありで複雑、たぶん。今回はローカルの環境でしか使用しないので、v2を使用。

### SetRequest 設定を送信する

```c#:設定値の送信

private void SetRequest(string oid, int val)
        {
            // Snmp v2
            VersionCode version = VersionCode.V2;

            var receiver = new IPEndPoint(IPAddress.Parse(ip), snmpProt);
            Discovery discovery = Messenger.GetNextDiscovery(SnmpType.GetRequestPdu);
            ReportMessage report = discovery.GetResponse(timeout, receiver);

            // int型のデータを送信する場合
            ISnmpData data = new Integer32(val);

            // string型のデータを送信する場合
//            ISnmpData data = new OctetString(val);

            var test = new Variable(new ObjectIdentifier(oid), data);
            var vList = new List<Variable>();
            vList.Add(test);

            var request = new SetRequestMessage(Messenger.NextRequestId, version, new OctetString(community), vList);

            ISnmpMessage reply = request.GetResponse(timeout, receiver);

            if (reply.Pdu().ErrorStatus.ToInt32() != 0)
            {
                throw ErrorException.Create(
                    "error in response",
                    receiver.Address,
                    reply);
            }

            // 結果を出力
            foreach (Variable v in reply.Pdu().Variables)
            {
                Console.WriteLine(v);
            }
        }
```

### 設定を取得する
ここではint型の設定を返している。

```c#:設定値の取得

        private int GetRequest(string oid)
        {

            VersionCode version = VersionCode.V2;

            var receiver = new IPEndPoint(IPAddress.Parse(ip), snmpProt);
            Discovery discovery = Messenger.GetNextDiscovery(SnmpType.GetRequestPdu);
            ReportMessage report = discovery.GetResponse(timeout, receiver);

            // 取得したいOID
            var test = new Variable(new ObjectIdentifier(oid));
            var vList = new List<Variable>();
            vList.Add(test);

            var request = new GetRequestMessage(Messenger.NextRequestId, version, new OctetString(community), vList);

            ISnmpMessage reply = request.GetResponse(timeout, receiver);

            if (reply.Pdu().ErrorStatus.ToInt32() != 0)
            {
                throw ErrorException.Create(
                    "error in response",
                    receiver.Address,
                    reply);
            }

            // 結果を出力
            foreach (Variable v in reply.Pdu().Variables)
            {
                Console.WriteLine(v);                
            }

            return int.Parse(reply.Pdu().Variables[0].Data.ToString());
        }
    }
```
## その他はまったポイント
ルーターの設定画面にラウザでアクセスしている状態(ログインしている)で、プログラムからコマンドを投げてもうまく反映がされなかった。コマンドを投げるときは、一旦ログアウトしてから、コマンドを投げる。その後、もう一度ルーターにログインして、値が変更されているかを確認する、といったことをやらなければならなかった。これも気づくまでに時間がかかった。

## 参考
- [2.6.11 拡張MIBオブジェクトの定義例](http://itdoc.hitachi.co.jp/manuals/3020/30203T0401/SNMP0050.HTM)
- [SNMP Library Documentation](https://docs.sharpsnmp.com/)