やりたいこと
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
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
インストール方法はここに書いてある。
usingLextm.SharpSnmpLib;usingLextm.SharpSnmpLib.Messaging;
- SNMPv2を使用した。v2はわりとシンプル。v3は認証などもありで複雑、たぶん。今回はローカルの環境でしか使用しないので、v2を使用。
SetRequest 設定を送信する
privatevoidSetRequest(stringoid,intval){// Snmp v2VersionCodeversion=VersionCode.V2;varreceiver=newIPEndPoint(IPAddress.Parse(ip),snmpProt);Discoverydiscovery=Messenger.GetNextDiscovery(SnmpType.GetRequestPdu);ReportMessagereport=discovery.GetResponse(timeout,receiver);// int型のデータを送信する場合ISnmpDatadata=newInteger32(val);// string型のデータを送信する場合// ISnmpData data = new OctetString(val);vartest=newVariable(newObjectIdentifier(oid),data);varvList=newList<Variable>();vList.Add(test);varrequest=newSetRequestMessage(Messenger.NextRequestId,version,newOctetString(community),vList);ISnmpMessagereply=request.GetResponse(timeout,receiver);if(reply.Pdu().ErrorStatus.ToInt32()!=0){throwErrorException.Create("error in response",receiver.Address,reply);}// 結果を出力foreach(Variablevinreply.Pdu().Variables){Console.WriteLine(v);}}
設定を取得する
ここではint型の設定を返している。
privateintGetRequest(stringoid){VersionCodeversion=VersionCode.V2;varreceiver=newIPEndPoint(IPAddress.Parse(ip),snmpProt);Discoverydiscovery=Messenger.GetNextDiscovery(SnmpType.GetRequestPdu);ReportMessagereport=discovery.GetResponse(timeout,receiver);// 取得したいOIDvartest=newVariable(newObjectIdentifier(oid));varvList=newList<Variable>();vList.Add(test);varrequest=newGetRequestMessage(Messenger.NextRequestId,version,newOctetString(community),vList);ISnmpMessagereply=request.GetResponse(timeout,receiver);if(reply.Pdu().ErrorStatus.ToInt32()!=0){throwErrorException.Create("error in response",receiver.Address,reply);}// 結果を出力foreach(Variablevinreply.Pdu().Variables){Console.WriteLine(v);}returnint.Parse(reply.Pdu().Variables[0].Data.ToString());}}
その他はまったポイント
ルーターの設定画面にラウザでアクセスしている状態(ログインしている)で、プログラムからコマンドを投げてもうまく反映がされなかった。コマンドを投げるときは、一旦ログアウトしてから、コマンドを投げる。その後、もう一度ルーターにログインして、値が変更されているかを確認する、といったことをやらなければならなかった。これも気づくまでに時間がかかった。