Go言語でsFlowのパケットを受信する方法

# はじめに

Go言語でsFlowのパッケットを受信して処理する方法の紹介です。sFlowのサーバーを開発するGo言語のパッケージはありますが、うまく動かなかったので、sFlowのパケットをデコードするパッケージ

https://github.com/Cistern/sflow

を使ってサーバーを作る方法を紹介します。

# sFlowパケット受信プログラム

```go
package main

import (
	"bytes"
	"log"
	"net"

	"github.com/Cistern/sflow"
)

func main() {
	sv, err := net.ListenPacket("udp", ":6343")
	if err != nil {
		log.Fatalln(err)
	}
	defer sv.Close()
	data := make([]byte, 8192)
	for {
		l, ra, err := sv.ReadFrom(data)
		if err != nil {
			log.Println(err)
			continue
		}
		r := bytes.NewReader(data[:l])
		d := sflow.NewDecoder(r)
		dg, err := d.Decode()
		if err != nil {
			log.Println(err)
			continue
		}
		raIP := ""
		switch a := ra.(type) {
		case *net.UDPAddr:
			raIP = a.IP.String()
		case *net.TCPAddr:
			raIP = a.IP.String()
		}
		for _, sample := range dg.Samples {
			switch s := sample.(type) {
			case *sflow.CounterSample:
				log.Printf("CounterSample from %s:", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}
			case *sflow.FlowSample:
				log.Printf("FlowSample from %s", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}
			case *sflow.EventDiscardedPacket:
				log.Printf("EventDiscardedPacket from %s", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}
			default:
				log.Printf("%v form %s", s, raIP)
			}
		}
	}
}

```

このプログラムのポイントは

### UDPでsFlowのポートを受信できるようにする

```go
	sv, err := net.ListenPacket("udp", ":6343")
	if err != nil {
		log.Fatalln(err)
	}
	defer sv.Close()

```

### 　受信したデータをsFlowパッケージでデコードする

```go
		l, ra, err := sv.ReadFrom(data)
		if err != nil {
			log.Println(err)
			continue
		}
		r := bytes.NewReader(data[:l])
		d := sflow.NewDecoder(r)
		dg, err := d.Decode()
		if err != nil {
			log.Println(err)
			continue
		}

```

### デコードしたデータに基づいて処理する

```go
		for _, sample := range dg.Samples {
			switch s := sample.(type) {
			case *sflow.CounterSample:
				log.Printf("CounterSample from %s:", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}
			case *sflow.FlowSample:
				log.Printf("FlowSample from %s", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}
			case *sflow.EventDiscardedPacket:
				log.Printf("EventDiscardedPacket from %s", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}
			default:
				log.Printf("%v form %s", s, raIP)
			}
		}
```

デコードしたデータには、

- CounterSample(CPUやメモリーの使用率）
- FlowSample（パケットのサンプル）
- EventDiscardedPacket（破棄したパケットのサンプル）

があります。


# 実行結果

```terminal

go run main.go
2024/09/29 17:10:15 CounterSample from 192.168.1.32:
2024/09/29 17:10:15 HostDiskCounters: {Total:10196652032 Free:8712331264 MaxUsedPercent:2.039e-42 Reads:49837 BytesRead:1260391424 ReadTime:136338 Writes:3332846 BytesWritten:15576254464 WriteTime:3325584}
2024/09/29 17:10:15 HostMemoryCounters: {Total:498462720 Free:73256960 Shared:0 Buffers:524288 Cached:179838976 SwapTotal:0 SwapFree:0 PageIn:814353 PageOut:7605593 SwapIn:0 SwapOut:0}
2024/09/29 17:10:15 HostCPUCounters: {Load1m:0.08 Load5m:0.08 Load15m:0.02 ProcessesRunning:0 ProcessesTotal:153 NumCPU:1 SpeedCPU:3392 Uptime:2028012 CPUUser:2308970 CPUNice:14820 CPUSys:2891590 CPUIdle:2015687000 CPUWio:214730 CPUIntr:0 CPUSoftIntr:338560 Interrupts:107210281 ContextSwitches:117161266 CPUSteal:56590 CPUGuest:0 CPUGuestNice:0}
2024/09/29 17:10:15 HostNetCounters: {BytesIn:107098 PacketsIn:976 ErrorsIn:23 DropsIn:0 BytesOut:182648 PacketsOut:835 ErrorsOut:0 DropsOut:0}
2024/09/29 17:10:17 FlowSample from 192.168.1.32
2024/09/29 17:10:17 RawPacketFlow: {Protocol:1 FrameLength:370 Stripped:4 HeaderSize:128 Header:[80 0 0 4 0 1 80 0 0 6 0 0 8 0 69 0 1 96 161 216 0 0 64 17 9 233 10 30 1 12 192 168 1 250 12 213 8 8 1 76 31 4 0 9 0 4 120 226 28 166 102 249 11 104 0 5 1 16 0 0 0 1 1 2 0 76 0 0 0 0 0 0 0 222 0 0 0 0 0 0 0 222 0 0 0 3 0 0 0 3 120 223 70 212 120 223 70 212 0 161 233 159 0 0 0 1 17 0 0 0 20 0 0 48 68 0 0 0 0 0 0 0 0 14 4 195 0 10]}
2024/09/29 17:10:17 FlowSample from 192.168.1.32
2024/09/29 17:10:17 RawPacketFlow: {Protocol:1 FrameLength:74 Stripped:4 HeaderSize:70 Header:[80 0 0 4 0 0 0 37 54 171 119 83 8 0 69 0 0 56 204 75 0 0 63 1 225 173 192 168 1 250 10 30 1 12 3 3 230 211 0 0 0 0 69 0 1 96 161 216 0 0 63 17 10 233 10 30 1 12 192 168 1 250 12 213 8 8 1 76 0 0]}
2024/09/29 17:10:18 FlowSample from 192.168.1.32
2024/09/29 17:10:18 RawPacketFlow: {Protocol:1 FrameLength:64 Stripped:4 HeaderSize:60 Header:[1 128 194 0 0 0 108 205 214 195 36 97 0 39 66 66 3 0 0 2 2 124 128 0 108 205 214 195 36 95 0 0 0 0 128 0 108 205 214 195 36 95 128 4 0 0 20 0 2 0 15 0 0 0 0 0 0 0 0 0]}
2024/09/29 17:10:18 FlowSample from 192.168.1.32
2024/09/29 17:10:18 ExtendedSwitchFlow: {SourceVlan:0 SourcePriority:0 DestinationVlan:0 DestinationPriority:0}
2024/09/29 17:10:18 RawPacketFlow: {Protocol:1 FrameLength:146 Stripped:4 HeaderSize:128 Header:[80 0 0 4 0 0 0 37 54 171 119 83 8 0 69 0 3 29 103 109 0 0 57 17 143 109 198 41 0 4 192 168 1 32 0 53 136 185 3 9 83 11 156 87 128 0 0 1 0 0 0 8 0 13 3 111 114 103 0 0 1 0 1 192 12 0 2 0 1 0 2 163 0 0 25 2 97 50 3 111 114 103 11 97 102 105 108 105 97 115 45 110 115 116 4 105 110 102 111 0 192 12 0 2 0 1 0 2 163 0 0 21 2 98 50 3 111 114 103 11 97 102 105 108 105 97 115 45]}
```

のような感じになります。


# 余談

私の開発したTWSNMP FC/FKでは、この方法でsFlowのパケットを処理しています。
このsFlowのパケットをデコードするパッケージを使っている理由は

https://note.com/twsnmp/n/nd27fb9ce2525

に詳しく書いています。

このパッケージ開発をちょっとだけお手伝いしました。

https://note.com/twsnmp/n/n4753878e2eba