やりたいこと
WIFIルーターの電波on/offをリモートで行う。
前提となる知識
SNMP(Simple Network Management Protocol)
- ネットワーク機器を管理するために用いられるプロトコル。今回でいうと、ルーターの設定値を読み出したり、電波をon/offしたり。
- ルーターの設定をSNMPプロトコルを通じて変更が可能 (変更が許可されているもののみ)。
- ルーターの設定値を読み出すことも可能。
MIBファイル形式
- MIBファイルにはルーターの各設定について、OID(Object ID)が何で、データ型が何なのか、設定許可されているかどうかなどが記載されている。
- ルーターの各設定項目について、それぞれObject ID(OID)が設定されている(メーカー独自)ため、ルーターのメーカーからMIBファイルを入手する必要がある。もしくはルーターの管理画面からダウンロードできるのかもしれない。
- MIBファイル形式について
- Q:SNMPv2形式の拡張MIBファイルの基本的な文法
OID(Object ID)について
設定値がツリー構造になっており、数字を順番に辿っていくと、ある設定値にたどり着く。たとえば、以下の例だとxxはベンダー名が割り当てられている。それ以降はモデル名だったり、設定値だったりと続いていく。これはベンダー毎に定義がされている。
1.3.6.1.4.1.xx
実際のMIBファイルはテキストになっているが、解読するのは結構大変だった。以下は実際の例。
使用したツール
TWSNMP
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
インストール方法はここに書いてある。
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
- SNMPv2を使用した。v2はわりとシンプル。v3は認証などもありで複雑、たぶん。今回はローカルの環境でしか使用しないので、v2を使用。
SetRequest 設定を送信する
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型の設定を返している。
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());
}
}
その他はまったポイント
ルーターの設定画面にラウザでアクセスしている状態(ログインしている)で、プログラムからコマンドを投げてもうまく反映がされなかった。コマンドを投げるときは、一旦ログアウトしてから、コマンドを投げる。その後、もう一度ルーターにログインして、値が変更されているかを確認する、といったことをやらなければならなかった。これも気づくまでに時間がかかった。