Go言語のSNMPパッケージgosnmpのサンプルプログラムをSNMPv3対応する方法

# はじめに

前回までの記事

https://qiita.com/twsnmp/items/2986b696cced09820291

でgosnmpパッケージのサンプルプログラムについて紹介しました。
これまでの説明は、デフォルトのSNMPv2cによる通信でした。
セキュリティーを重視する人は認証や暗号化に対応したSNMPv3で通信したいと思っているでしょう。そこで、今回は、このサンプルプログラムをSNMPv3対応にする方法を説明します。

# SNMPv3対応

SNMPv3対応に変更したソースコードは

```diff_go
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
	}
	// Default is a pointer to a GoSNMP struct that contains sensible defaults
	// eg port 161, community public, etc
	g.Default.Target = "192.168.1.210"
	// g.Default.Community = "public"
	// g.Default.Port = 161
+	g.Default.Version = g.Version3
+	g.Default.SecurityModel = g.UserSecurityModel
+	g.Default.MsgFlags = g.AuthPriv
+	g.Default.SecurityParameters = &g.UsmSecurityParameters{UserName: "user_name",
+		AuthenticationProtocol:   g.SHA,
+		AuthenticationPassphrase: "sha_pass",
+		PrivacyProtocol:          g.AES,
+		PrivacyPassphrase:        "aes_pass",
+	}
	err = g.Default.Connect()
	if err != nil {
		log.Fatalf("Connect() err: %v", err)
	}
	defer g.Default.Conn.Close()

	//	oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"}
	oids := []string{m.NameToOID("sysContact.0"), m.NameToOID("sysServices.0")}
	result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS
	if err2 != nil {
		log.Fatalf("Get() err: %v", err2)
	}

	for i, variable := range result.Variables {
		//		fmt.Printf("%d: oid: %s", i, variable.Name)
		fmt.Printf("%d: oid: %s name:%s = ", i, variable.Name, m.OIDToName(variable.Name))

		// the Value of each variable returned by Get() implements
		// interface{}. You could do a type switch...
		switch variable.Type {
		case g.OctetString:
			bytes := variable.Value.([]byte)
			fmt.Printf("string: %s\n", string(bytes))
		default:
			// ... or often you're just interested in numeric values.
			// ToBigInt() will return the Value as a BigInt, for plugging
			// into your calculations.
			fmt.Printf("number: %d\n", g.ToBigInt(variable.Value))
		}
	}
}
```

9行加えただけです。

# 変更のポイント

### バージョンの指定

SNMPv3の指定は

```go
g.Default.Version = g.Version3
```
の行です。

他に以下のバージョンが指定できます。

```go
// SnmpVersion 1, 2c and 3 implemented
const (
	Version1  SnmpVersion = 0x0
	Version2c SnmpVersion = 0x1
	Version3  SnmpVersion = 0x3
)

```

デフォルトは`Version2c`です。

### セキュリティーモデル

セキュリティーモデルは

```go
g.Default.SecurityModel = g.UserSecurityModel
```

です。ユーザーベースセキュリティーモデルしか選択肢はありません。

### メッセージフラグ

認証と暗号化の設定フラグは

```go
g.Default.MsgFlags = g.AuthPriv

```

メッセージフラグは他に、

```go
// Possible values of SnmpV3MsgFlags
const (
	NoAuthNoPriv SnmpV3MsgFlags = 0x0 // No authentication, and no privacy
	AuthNoPriv   SnmpV3MsgFlags = 0x1 // Authentication and no privacy
	AuthPriv     SnmpV3MsgFlags = 0x3 // Authentication and privacy
	Reportable   SnmpV3MsgFlags = 0x4 // Report PDU must be sent.
)
```

のように定義されています。認証の有無、暗号化の有無の組み合わせだけ使います。`Reportable`は、使ったことがありません。（今度調べます。）


### セキュリティーパラメータ

認証や暗号化のパラメータは

```go
	g.Default.SecurityParameters = &g.UsmSecurityParameters{UserName: "user_name",
		AuthenticationProtocol:   g.SHA,
		AuthenticationPassphrase: "sha_pass",
		PrivacyProtocol:          g.AES,
		PrivacyPassphrase:        "aes_pass",
	}
```

ユーザー名、認証の方式（HASHアルゴリズム）、認証のパスワード、暗号化のアルゴリズム、暗号のアルゴリズムを指定します。


# NET-SNMPのSNMPv3エージェントでテストする

### SNMPv3エージェントの起動

https://qiita.com/skouno25/items/040476034e86e6fe1f71

の記事の通りにSNMPエージェントを設定すれば、SNMPv3で通信できます。

### テスト結果

SNMPv3のエージェントを先の手順で起動して、そのアドレスを指定すれば、

```terminal
$go run main.go
0: oid: .1.3.6.1.2.1.1.4.0 name:sysContact.0 = string: twsnmp@gmail.com
1: oid: .1.3.6.1.2.1.1.7.0 name:sysServices.0 = number: 72
```
のようになります。


# 余談

SNMPv3の認証、暗号化などについては


https://note.com/twsnmp/n/n389fc4d8fc66?magazine_key=med8200058170

に詳しく書きました。