はじめに
Go言語でsFlowのパッケットを受信して処理する方法の紹介です。sFlowのサーバーを開発するGo言語のパッケージはありますが、うまく動かなかったので、sFlowのパケットをデコードするパッケージ
を使ってサーバーを作る方法を紹介します。
sFlowパケット受信プログラム
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のポートを受信できるようにする
sv, err := net.ListenPacket("udp", ":6343")
if err != nil {
log.Fatalln(err)
}
defer sv.Close()
受信したデータをsFlowパッケージでデコードする
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
}
デコードしたデータに基づいて処理する
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(破棄したパケットのサンプル)
があります。
実行結果
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のパケットをデコードするパッケージを使っている理由は
に詳しく書いています。
このパッケージ開発をちょっとだけお手伝いしました。