1. はじめに
SNMPエージェントのソフトウェアの開発に利用されるフリーのSNMPパッケージで、
代表的なものとしてはNet-SNMPがあります。
http://net-snmp.sourceforge.net/
この記事では、
Net-SNMPに付属しているsnmpwalkコマンドを使って、ルーティング情報が格納されている
ipCidrRouteTable(.1.3.6.1.2.1.4.24.4)
ipCidrRouteEntry(.1.3.6.1.2.1.4.24.4.1)
のsnmpwalk結果をPowerShellのスクリプトで処理し、テーブル形式での出力を得る処理の作成例を紹介します。
テーブル形式での出力結果は、下記のような形式です。
取得結果例1(テーブル形式)
Destination Netmask NextHop ifIndex Type Proto Tos Age Info
10.1.15.0 255.255.255.0 0.0.0.0 99 local local 0 169996 .0.0
10.1.15.254 255.255.255.255 0.0.0.0 99 local local 0 169996 .0.0
10.15.0.0 255.255.255.0 0.0.0.0 99 local local 0 169996 .0.0
10.15.0.254 255.255.255.255 0.0.0.0 99 local local 0 169996 .0.0
10.15.10.0 255.255.255.0 0.0.0.0 99 local local 0 169996 .0.0
10.15.10.254 255.255.255.255 0.0.0.0 99 local local 0 169996 .0.0
10.20.25.0 255.255.255.0 0.0.0.0 10501 remote ospf 0 0 .0.0
10.20.25.254 255.255.255.255 0.0.0.0 20567 local local 0 170000 .0.0
10.16.0.0 255.255.255.0 0.0.0.0 99 local local 0 169996 .0.0
10.16.0.254 255.255.255.255 0.0.0.0 99 local local 0 169996 .0.0
取得結果例2(csv形式)
Destination;Netmask;NextHop;ifIndex;Type;Proto;Tos;Age;Info
10.1.15.0;255.255.255.0;0.0.0.0;99;local;local;0;169996;.0.0
10.1.15.254;255.255.255.255;0.0.0.0;99;local;local;0;169996;.0.0
10.15.0.0;255.255.255.0;0.0.0.0;99;local;local;0;169996;.0.0
10.15.0.254;255.255.255.255;0.0.0.0;99;local;local;0;169996;.0.0
10.15.10.0;255.255.255.0;0.0.0.0;99;local;local;0;169996;.0.0
10.15.10.254;255.255.255.255;0.0.0.0;99;local;local;0;169996;.0.0
10.20.25.0;255.255.255.0;0.0.0.0;10501;remote;ospf;0;0;.0.0
10.20.25.254;255.255.255.255;0.0.0.0;20567;local;local;0;170000;.0.0
10.16.0.0;255.255.255.0;0.0.0.0;99;local;local;0;169996;.0.0
10.16.0.254;255.255.255.255;0.0.0.0;99;local;local;0;169996;.0.0
完成例
PowerShellスクリプトの完成例は以下にも配置しています。
-
ipCidrRouteTable版
https://github.com/NobuyukiInoue/PS_convert_ipCidrRouteTable -
ifEntry版
https://github.com/NobuyukiInoue/PS_convert_ifEntry
ちなみに、GUI版のほとんどのSNMPマネージャ製品では、簡単にメニュー等から選択するだけで表形式でみることができます。
しかし、SNMPマネージャのデバッグ時や、障害が発生していてSNMPマネージャが利用できない場合などは、
直接、対象のNW機器に対してsnmpwalkしたいときがありますよね。
snmpwalkを利用すれば、引数で指定したOID配下のMIBを再帰的にgetnextするので、下記のように格納順に結果が得られますが、同一レコードをグループ化して見たいときは、表計算ソフトに張り付けたりする作業が必要となるなど、いろいろと不便です。
PS D:\PS_convert_ipCidrRouteTable> snmpwalk -On -c public -v2c 10.15.10.254 .1.3.6.1.2.1.4.24.4.1
.1.3.6.1.2.1.4.24.4.1.1.10.1.15.0.255.255.255.0.0.0.0.0.0 = IpAddress: 10.1.15.0
.1.3.6.1.2.1.4.24.4.1.1.10.1.15.254.255.255.255.255.0.0.0.0.0 = IpAddress: 10.1.15.254
(中略)
.1.3.6.1.2.1.4.24.4.1.2.10.1.15.0.255.255.255.0.0.0.0.0.0 = IpAddress: 255.255.255.0
.1.3.6.1.2.1.4.24.4.1.2.10.1.15.254.255.255.255.255.0.0.0.0.0 = IpAddress: 255.255.255.255
(中略)
.1.3.6.1.2.1.4.24.4.1.3.10.1.15.0.255.255.255.0.0.0.0.0.0 = INTEGER: 0
.1.3.6.1.2.1.4.24.4.1.3.10.1.15.254.255.255.255.255.0.0.0.0.0 = INTEGER: 0
(中略)
.1.3.6.1.2.1.4.24.4.1.4.10.1.15.0.255.255.255.0.0.0.0.0.0 = IpAddress: 0.0.0.0
.1.3.6.1.2.1.4.24.4.1.4.10.1.15.254.255.255.255.255.0.0.0.0.0 = IpAddress: 0.0.0.0
(中略)
そこで、snmpwalkの実行結果をPowerShellのスクリプトで処理し、テーブル形式で出力してみることにします。
pythonやrubyなどでは、snmp用のライブラリが用意されています。
PowerShellでも、外部のDLLやクラスライブラリを利用すれば、もう少し簡単に処理できるかもしれませんが、今回は、snmpwalkの実行結果を利用して、PowerShellスクリプト単体で処理することにしました。
2. PowerShellスクリプトの作成
2つのクラスと2つのハッシュテーブルを用意します。
2-1. IfValuesクラス
IfValuesクラスは、ipCidrRouteEntry(.1.3.6.1.2.1.4.24.4.1) 配下のMIBの
- OID
- パターンマッチで使用するためのOIDを正規表現化したもの
- 対象OIDの名前
- 読み込み時の検証で使用するカウンタ
- 初期化用のInitメソッド
などをとりまとめたオブジェクト用のクラスです。
class IfValues {
[string] $OID
[string] $OIDRegular
[string] $Name
[int] $count
Init($oid, $name) {
$this.Name = $name
$this.OID = $oid
$this.OIDRegular = "^" + $this.OID.Replace("." ,"\.")
$this.count = 0
}
}
2-2. RouteTableクラス
RouteTableクラスは、ipCidrRouteEntry(.1.3.6.1.2.1.4.24.4.1) 配下のMIBの
各項目と型を定義したクラスです。1つの宛先の情報をとりまとめたもの(1レコード)に相当します。
snmpwalkの実行結果は文字列ですが、値の前に型情報も含まれているので、
INTEGER32はint型、IpAddress型やOID型はstring 型で読み込むことにします。
実行結果は複数の宛先から構成されますので、実行時にはこのオブジェクトの配列を用意することになります。
class RouteTable {
[string] $ipCidrRouteOID
[string] $ipCidrRouteDest
[string] $ipCidrRouteMask
[int] $ipCidrRouteTos
[string] $ipCidrRouteNextHop
[int] $ipCidrRouteIfIndex
[int] $ipCidrRouteType
[string] $ipCidrRouteTypeName
[int] $ipCidrRouteProto
[string] $ipCidrRouteProtoName
[int] $ipCidrRouteAge
[string] $ipCidrRouteInfo
[int] $ipCidrRouteNextHopAS
[int] $ipCidrRouteMetric1
[int] $ipCidrRouteMetric2
[int] $ipCidrRouteMetric3
[int] $ipCidrRouteMetric4
[int] $ipCidrRouteMetric5
[int] $ipCidrRouteStatus
}
2-3. 各OID用のIfIndexオブジェクトの生成
クラスの定義が終わったので、ipCidrRouteEntry(.1.3.6.1.2.1.4.24.4.1) 配下用のオブジェクトを生成します。
$ipCidrRouteOID = New-Object IfValues
$ipCidrRouteOID.Init(".1.3.6.1.2.1.4.24.4.", "ipCidrRouteOID")
$ipCidrRouteDest = New-Object IfValues
$ipCidrRouteDest.Init(".1.3.6.1.2.1.4.24.4.1.1.", "ipCidrRouteDest")
$ipCidrRouteMask = New-Object IfValues
$ipCidrRouteMask.Init(".1.3.6.1.2.1.4.24.4.1.2.", "ipCidrRouteMask")
$ipCidrRouteTos = New-Object IfValues
$ipCidrRouteTos.Init(".1.3.6.1.2.1.4.24.4.1.3.", "ipCidrRouteTos")
$ipCidrRouteNextHop = New-Object IfValues
$ipCidrRouteNextHop.Init(".1.3.6.1.2.1.4.24.4.1.4.", "ipCidrRouteNextHop")
$ipCidrRouteIfIndex = New-Object IfValues
$ipCidrRouteIfIndex.Init(".1.3.6.1.2.1.4.24.4.1.5.", "ipCidrRouteIfIndex")
$ipCidrRouteType = New-Object IfValues
$ipCidrRouteType.Init(".1.3.6.1.2.1.4.24.4.1.6.", "ipCidrRouteType")
$ipCidrRouteProto = New-Object IfValues
$ipCidrRouteProto.Init(".1.3.6.1.2.1.4.24.4.1.7.", "ipCidrRouteProto")
$ipCidrRouteAge = New-Object IfValues
$ipCidrRouteAge.Init(".1.3.6.1.2.1.4.24.4.1.8.", "ipCidrRouteAge")
$ipCidrRouteInfo = New-Object IfValues
$ipCidrRouteInfo.Init(".1.3.6.1.2.1.4.24.4.1.9.", "ipCidrRouteInfo")
...
...
2-4. typeとProtocolの定義値
ipCidrRouteType(1.3.6.1.2.1.4.24.4.1.6)
ipCidrRouteProto(1.3.6.1.2.1.4.24.4.1.7)
については、数値のまま結果を表示すると解りにくいので、
MIB定義ファイルに記載されている値をハッシュテーブルとして用意しておき、
取得した値からプロトコル名に変換できるようにしておきます。
今回のスクリプトでは-Onオプションに合わせて作っていますが、
snmpwalk実行時に、-Onオプションを指定しないという手もありますね。
$typeTable = @{
1 = "other"
2 = "reject"
3 = "local"
4 = "remote"
}
$protoTable = @{
1 = "other"
2 = "local"
3 = "netmgmt"
4 = "icmp"
5 = "egp"
6 = "ggp"
7 = "hello"
8 = "rip"
9 = "isIs"
10 = "esIs"
11 = "ciscoIgrp"
12 = "bbnSpfIgp"
13 = "ospf"
14 = "bgp"
15 = "idpr"
16 = "ciscoEigrp"
}
2-5. 読み込み処理
snmpwalk からパイプで渡された実行結果は、$input に格納されていますので、
foreach で1行ずつ読み込み、OID番号 が一致するフィールドに格納していきます。
$resultTable は、結果を格納するための RouteTable型 の配列です。
$resultTable = @()
foreach ($line in $input) {
# Write-Output $line
switch -Regex ($line) {
$ipCidrRouteDest.OIDRegular {
$line = $line.Replace($ipCidrRouteDest.OID, "")
$flds = $line -split " = "
if ($flds.Length -ge 2) {
$resultTable += New-Object RouteTable
$resultTable[$resultTable.Lenght - 1].ipCidrRouteOID = $flds[0].Replace($ipCidrRouteDest.OID, "")
$resultTable[$resultTable.Lenght - 1].ipCidrRouteDest = $flds[1].Replace("IpAddress: ", "")
}
else {
Write-Output "$ipCidrRouteDest.OID Check Error"
return
}
break
}
$ipCidrRouteMask.OIDRegular {
$line = $line.Replace($ipCidrRouteMask.OID, "")
$flds = $line -split " = "
if (($flds.Length -ge 2) -And ($flds[0] -eq $resultTable[$ipCidrRouteMask.count].ipCidrRouteOID)) {
$resultTable[$ipCidrRouteMask.count].ipCidrRouteMask = $flds[1].Replace("IpAddress: ", "")
$ipCidrRouteMask.count++
}
else {
Write-Output "$ipCidrRouteMask.OID Check Error"
return
}
break
}
...
...
2-6. typeとprotocolから定義値を読み込む
type と protocol については、
$resultTable[$i].ipCidrRouteType
$resultTable[$i].ipCidrRouteProto
に格納されている値から、ハッシュテーブルを参照して定義値を取得します。
取得結果は、それぞれ、
$resultTable[$i].ipCidrRouteTypeName
$resultTable[$i].ipCidrRouteProtoName
に格納しておきます。
for ($i = 0; $i -lt $resultTable.Length; $i++) {
$resultTable[$i].ipCidrRouteTypeName = $typeTable[$resultTable[$i].ipCidrRouteType]
}
for ($i = 0; $i -lt $resultTable.Length; $i++) {
$resultTable[$i].ipCidrRouteProtoName = $protoTable[$resultTable[$i].ipCidrRouteProto]
}
2-7. 結果の表示処理
配列**$resultTable** の全レコード、各レコードを標準出力に書き出します。
$IFSの内容によって、出力幅を固定して出力したり、
デリミタ文字を指定してCSV形式での出力もできるようにしています。
if ($IFS -eq "") {
$format = "{0,-20}{1,-20}{2,-20}{3,-10}{4,-10}{5,-10}{6,-10}{7,-10}{8,-10}"
}
else {
$format = "{0}$IFS{1}$IFS{2}$IFS{3}$IFS{4}$IFS{5}$IFS{6}$IFS{7}$IFS{8}"
}
Write-Output ($format -f `
"Destination" `
, "Netmask" `
, "NextHop" `
, "ifIndex" `
, "Type" `
, "Proto" `
, "Tos" `
, "Age" `
, "Info" `
)
for ($i = 0; $i -lt $resultTable.Length; $i++) {
Write-Output ($format -f `
$resultTable[$i].ipCidrRouteDest `
, $resultTable[$i].ipCidrRouteMask `
, $resultTable[$i].ipCidrRouteNextHop `
, $resultTable[$i].ipCidrRouteIfIndex `
, $resultTable[$i].ipCidrRouteTypeName `
, $resultTable[$i].ipCidrRouteProtoName `
, $resultTable[$i].ipCidrRouteTos `
, $resultTable[$i].ipCidrRouteAge `
, $resultTable[$i].ipCidrRouteInfo `
)
}
3. 実行例
実行する場合は、以下のような書式でsnmpwalkの実行結果を、PowerShellスクリプトに渡します。
書式
snmpwalk -On -v 2c -c <community> <target_ip> .1.3.6.1.2.1.4.24.4 | .\convert_ipCidrRouteTable.ps1 [IFS(delimiter)]
実行例1(テーブル形式)
snmpwalk -On -v 2c -c public 10.1.15.254 .1.3.6.1.2.1.4.24.4 | .\PS_convert_ipCidrRouteTable.ps1
実行例2(コンマ区切り(csv形式))
snmpwalk -On -v 2c -c public 10.1.15.254 .1.3.6.1.2.1.4.24.4 | .\PS_convert_ipCidrRouteTable.ps1 ";"
実行例3(タブ区切り)
snmpwalk -On -v 2c -c public 10.1.15.254 .1.3.6.1.2.1.4.24.4 | .\PS_convert_ipCidrRouteTable.ps1 "\t"
毎回、OIDを指定してsnmpwalkを実行するのが面倒なのであれば、
下記のようなスクリプト内でsnmpwalkとPS_convert_ipCidrRouteTable.ps1を実行すると良いでしょう。
param($target, $community, $IFS)
if (-Not($target)) {
Write-Host "Usage :"$MyInvocation.MyCommand.Name"<target> <community>"
exit
}
if (-Not($community)) {
$community = "public"
}
if (-Not($IFS)) {
$IFS = ""
}
$OID = ".1.3.6.1.2.1.4.24.4"
$walkResult = snmpwalk -On -v 2c -c $community $target $OID
if ($walkResult -eq $NULL) {
Write-Output $walkResult
exit
}
$walkResult | ./PS_convert_ipCidrRouteTable.ps1 $IFS