はじめに
Go言語のSNMPパッケージgosnmpを紹介するシリーズの6回目です。
gosnmpパッケージのサンプルプログラムは、NET-SNMPのsnmpgetコマンドのような動作でした。今回はsnmpwalkコマンドの動作の作り方です。
の記事で紹介したOIDと名前の変換も使っています。
snmpwalkコマンドについて
snmpwalkは、NET-SNMPのコマンドです。グループ単位でMIBを取得できます。SNMPエージェントが対応しているMIBを全て取得する場合にも利用します。
$snmpwalk -v 2c -c public 192.168.1.210 system
SNMPv2-MIB::sysDescr.0 = STRING: Linux minipc 4.19.0-26-amd64 1 SMP Debian 4.19.304-1 (2024-01-09) x86_64
SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (17244672) 1 day, 23:54:06.72
SNMPv2-MIB::sysContact.0 = STRING: twsnmp@gmail.com
SNMPv2-MIB::sysName.0 = STRING: minipc
SNMPv2-MIB::sysLocation.0 = STRING: YMI2F
SNMPv2-MIB::sysServices.0 = INTEGER: 72
SNMPv2-MIB::sysORLastChange.0 = Timeticks: (1) 0:00:00.01
のようにsystemグループのMIBを1つのコマンドで取得できます。
SNMPのGetNextコマンドを繰り返してMIBツリーのsystem配下にあるMIB取得しています。
systemと指定している最初のMIBをisoとすれば、対象のSNMPエージェントが対応している全MIBを取得することができます。
snmpwalkをgosnmpを使って作る
snmpwalkをgosnmpで作ると
package main
import (
"fmt"
"log"
g "github.com/gosnmp/gosnmp"
mibdb "github.com/twsnmp/go-mibdb"
)
func main() {
m, err := mibdb.NewMIBDB("./mib.txt")
if err != nil {
fmt.Printf("NewMIBDB failed err=%v", err)
return
}
g.Default.Target = "192.168.1.210"
err = g.Default.Connect()
if err != nil {
log.Fatalf("Connect() err: %v", err)
}
defer g.Default.Conn.Close()
err = g.Default.Walk(m.NameToOID("system"), func(variable g.SnmpPDU) error {
fmt.Printf("oid: %s name:%s = ", variable.Name, m.OIDToName(variable.Name))
switch variable.Type {
case g.ObjectIdentifier:
fmt.Printf("oid: %s(%s)\n", m.OIDToName(variable.Value.(string)), variable.Value)
case g.OctetString:
bytes := variable.Value.([]byte)
fmt.Printf("string: %s\n", string(bytes))
default:
fmt.Printf("number: %d\n", g.ToBigInt(variable.Value))
}
return nil
})
if err != nil {
log.Fatalf("Walk() err: %v", err)
}
}
gosnmpには、Walkという関数があります。Walkは、GetNextで取得したMIBを1つづつCallバック関数に渡してくれます。
Callバック関数
err = g.Default.Walk(m.NameToOID("system"), func(variable g.SnmpPDU) error {
fmt.Printf("oid: %s name:%s = ", variable.Name, m.OIDToName(variable.Name))
switch variable.Type {
case g.ObjectIdentifier:
fmt.Printf("oid: %s(%s)\n", m.OIDToName(variable.Value.(string)), variable.Value)
case g.OctetString:
bytes := variable.Value.([]byte)
fmt.Printf("string: %s\n", string(bytes))
default:
fmt.Printf("number: %d\n", g.ToBigInt(variable.Value))
}
return nil
})
がCallバック関数です。この中で取得したMIBを表示すれば、snnmpwalkコマンドと同じような動作を実現できます。
実行結果
$go run main.go
oid: .1.3.6.1.2.1.1.1.0 name:sysDescr.0 = string: Linux minipc 4.19.0-26-amd64 1 SMP Debian 4.19.304-1 (2024-01-09) x86_64
oid: .1.3.6.1.2.1.1.2.0 name:sysObjectID.0 = oid: netSnmpAgentOIDs.10(.1.3.6.1.4.1.8072.3.2.10)
oid: .1.3.6.1.2.1.1.3.0 name:sysUpTimeInstance = number: 16366034
oid: .1.3.6.1.2.1.1.4.0 name:sysContact.0 = string: twsnmp@gmail.com
oid: .1.3.6.1.2.1.1.5.0 name:sysName.0 = string: minipc
oid: .1.3.6.1.2.1.1.6.0 name:sysLocation.0 = string: YMI2F
oid: .1.3.6.1.2.1.1.7.0 name:sysServices.0 = number: 72
oid: .1.3.6.1.2.1.1.8.0 name:sysORLastChange.0 = number: 1
snmptableコマンドについて
NET-SNMPにはテーブル形式のMIBを取得するsnmptableというコマンドがあります。
インターフェイスの情報ifTableをsnmpwalkで取得すると
$snmpwalk -v 2c -c public 192.168.1.210 ifTable
IF-MIB::ifIndex.1 = INTEGER: 1
IF-MIB::ifIndex.2 = INTEGER: 2
IF-MIB::ifIndex.3 = INTEGER: 3
IF-MIB::ifIndex.4 = INTEGER: 4
IF-MIB::ifIndex.5 = INTEGER: 5
IF-MIB::ifDescr.1 = STRING: lo
IF-MIB::ifDescr.2 = STRING: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller
IF-MIB::ifDescr.3 = STRING: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller
IF-MIB::ifDescr.4 = STRING: Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter
IF-MIB::ifDescr.5 = STRING: docker0
IF-MIB::ifType.1 = INTEGER: softwareLoopback(24)
IF-MIB::ifType.2 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.3 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.4 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.5 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifMtu.1 = INTEGER: 65536
IF-MIB::ifMtu.2 = INTEGER: 1500
IF-MIB::ifMtu.3 = INTEGER: 1500
IF-MIB::ifMtu.4 = INTEGER: 1500
IF-MIB::ifMtu.5 = INTEGER: 1500
IF-MIB::ifSpeed.1 = Gauge32: 10000000
IF-MIB::ifSpeed.2 = Gauge32: 1000000000
のように見やすくありません。snmptableで取得すると
$snmptable -v 2c -c public 192.168.1.210 ifTable
SNMP table: IF-MIB::ifTable
ifIndex ifDescr ifType ifMtu ifSpeed ifPhysAddress ifAdminStatus ifOperStatus ifLastChange ifInOctets ifInUcastPkts ifInNUcastPkts ifInDiscards ifInErrors ifInUnknownProtos ifOutOctets ifOutUcastPkts ifOutNUcastPkts ifOutDiscards ifOutErrors ifOutQLen ifSpecific
1 lo softwareLoopback 65536 10000000 up up 0:0:00:00.00 614573050 3237520 0 0 0 0 614573050 3237520 0 0 0 0 SNMPv2-SMI::zeroDotZero
2 Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller ethernetCsmacd 1500 1000000000 5c:85:7e:4a:9a:58 up up 0:0:00:00.00 30076806 64586983 75750 9840636 0 0 2996661367 121366896 0 0 0 0 SNMPv2-SMI::zeroDotZero
3 Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller ethernetCsmacd 1500 1000000000 5c:85:7e:4a:9a:59 up up 0:0:00:00.00 317343533 2127902930 22268130 78808 0 0 93935144 1114655 0 0 0 0 SNMPv2-SMI::zeroDotZero
4 Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter ethernetCsmacd 1500 0 0:e9:3a:89:8d:ff down down 0:0:00:00.00 0 0 0 0 0 0 0 0 0 0 0 0 SNMPv2-SMI::zeroDotZero
5 docker0 ethernetCsmacd 1500 0 2:42:89:3:c4:e2 up down 0:0:00:00.00 0 0 0 0 0 0 0 0 0 0 0 0 SNMPv2-SMI::zeroDotZero
ようにテーブル形式で表示できます。
TWSNMP FKの場合
TWSNMP FKで取得すると
のように表示できます。
gosnmpでsnmptableを作る
gosnmpでsnmptableを作ると
package main
import (
"fmt"
"log"
"strings"
g "github.com/gosnmp/gosnmp"
mibdb "github.com/twsnmp/go-mibdb"
)
func main() {
m, err := mibdb.NewMIBDB("./mib.txt")
if err != nil {
fmt.Printf("NewMIBDB failed err=%v", err)
return
}
g.Default.Target = "192.168.1.210"
err = g.Default.Connect()
if err != nil {
log.Fatalf("Connect() err: %v", err)
}
defer g.Default.Conn.Close()
tableMap := make(map[string]map[string]string)
nameMap := make(map[string]bool)
nameList := []string{}
err = g.Default.Walk(m.NameToOID("ifTable"), func(variable g.SnmpPDU) error {
name := m.OIDToName(variable.Name)
a := strings.SplitN(name, ".", 2)
if len(a) != 2 {
log.Fatalln("no index")
}
name = a[0]
index := a[1]
val := ""
switch variable.Type {
case g.ObjectIdentifier:
val = fmt.Sprintf("%s(%s)", m.OIDToName(variable.Value.(string)), variable.Value)
case g.OctetString:
if name == "ifPhysAddress" {
bytes := variable.Value.([]byte)
for i, b := range bytes {
if i > 0 {
val += ":"
}
val += fmt.Sprintf("%02X", b)
}
} else {
bytes := variable.Value.([]byte)
val = string(bytes)
}
default:
val = fmt.Sprintf("%d", g.ToBigInt(variable.Value))
}
if _, ok := nameMap[name]; !ok {
nameMap[name] = true
nameList = append(nameList, name)
}
if _, ok := tableMap[index]; ok {
tableMap[index][name] = val
} else {
tableMap[index] = make(map[string]string)
tableMap[index][name] = val
}
return nil
})
if err != nil {
log.Fatalf("Walk() err: %v", err)
}
fmt.Printf("index")
for _, n := range nameList {
fmt.Printf("\t%s", n)
}
fmt.Println("")
for i, m := range tableMap {
fmt.Printf("%s", i)
for _, n := range nameList {
if v, ok := m[n]; ok {
fmt.Printf("\t%s", v)
} else {
fmt.Printf("\t")
}
}
fmt.Println("")
}
}
Walk関数で取得したMIBをGo言語のMAPを使って並べ替えています。
$go run main.go
index ifIndex ifDescr ifType ifMtu ifSpeed ifPhysAddress ifAdminStatus ifOperStatus ifLastChange ifInOctets ifInUcastPkts ifInNUcastPkts ifInDiscards ifInErrors ifInUnknownProtos ifOutOctets ifOutUcastPkts ifOutNUcastPkts ifOutDiscards ifOutErrors ifOutQLen ifSpecific
5 5 docker0 6 1500 0 02:42:89:03:C4:E2 1 2 0 0 0 0 0 0 0 0 0 00 0 0 zeroDotZero(.0.0)
1 1 lo 24 65536 10000000 1 1 0 614573050 3237520 0 0 0 0 614573050 3237520 0 0 0 0 zeroDotZero(.0.0)
2 2 Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller 6 1500 1000000000 5C:85:7E:4A:9A:58 1 1 0 30524249 64592573 75761 9841794 0 0 3001538382 121384914 0 0 0 0 zeroDotZero(.0.0)
3 3 Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller 6 1500 1000000000 5C:85:7E:4A:9A:59 1 1 0 430705774 2128102302 22271244 78808 0 0 93945512 1114781 0 0 0 0 zeroDotZero(.0.0)
4 4 Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter 6 1500 0 00:E9:3A:89:8D:FF 2 2 0 0 00 0 0 0 0 0 0 0 0 0 zeroDotZero(.0.0)
ifPhysAddressは、そのまま文字列にすると文字化けするので
if name == "ifPhysAddress" {
bytes := variable.Value.([]byte)
for i, b := range bytes {
if i > 0 {
val += ":"
}
val += fmt.Sprintf("%02X", b)
}
} else {
bytes := variable.Value.([]byte)
val = string(bytes)
}
のようにMACアドレスのように表示する工夫をしています。