1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go言語でsFlowのFlowSampleをデコードする方法

Posted at

はじめに

前回の記事

の続きです。
前回の記事では、受信したFlowSampleを

        case *sflow.FlowSample:
				log.Printf("FlowSample from %s", raIP)
				for _, record := range s.Records {
					log.Printf("%+v", record)
				}

のような処理で表示していたので

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]}

のようにHeader部分が何のパケットなのかわかりません。
今回は、このHeaderをデコードして人の理解できる表示にする方法を紹介します。

gopacketパッケージ

Go言語でネットワークパケットを扱うパッケージが、gopacketです。

パケットのキャプチャーやパケットの生成の方法を紹介した記事がいくつかあります。

今回は、このパッケージをパケットのデコードに使います。

サンプルパケットデコード付きのsFlow受信プログラム

前回のソースコードを修正してパケットのデコード処理を追加したものが

package main

import (
	"bytes"
	"log"
	"net"

	"github.com/Cistern/sflow"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
)

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)
					switch fsr := record.(type) {
					case sflow.RawPacketFlow:
						rawPacketFlow(&fsr)
					}
				}

			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)
			}
		}
	}
}

func rawPacketFlow(r *sflow.RawPacketFlow) {
	packet := gopacket.NewPacket(r.Header, layers.LayerTypeEthernet, gopacket.Default)
	var record = make(map[string]interface{})
	ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
	if ethernetLayer != nil {
		if eth, ok := ethernetLayer.(*layers.Ethernet); ok {
			record["sourceMacAddress"] = eth.SrcMAC.String()
			record["destinationMacAddress"] = eth.SrcMAC.String()
		}
	}
	src := ""
	dst := ""
	bytes := 0
	ipv4Layer := packet.Layer(layers.LayerTypeIPv4)
	if ipv4Layer != nil {
		ip, ok := ipv4Layer.(*layers.IPv4)
		if !ok {
			return
		}
		src = ip.SrcIP.String()
		dst = ip.DstIP.String()
		bytes = int(ip.Length)
	} else {
		ipv6Layer := packet.Layer(layers.LayerTypeIPv6)
		if ipv6Layer != nil {
			ipv6, ok := ipv6Layer.(*layers.IPv6)
			if !ok {
				return
			}
			src = ipv6.SrcIP.String()
			dst = ipv6.DstIP.String()
			bytes = int(ipv6.Length)
		}
	}
	sp := 0
	dp := 0
	prot := 0
	flag := uint8(0)
	// UDP
	udpLayer := packet.Layer(layers.LayerTypeUDP)
	if udpLayer != nil {
		udp, ok := udpLayer.(*layers.UDP)
		if !ok {
			return
		}
		sp = int(udp.SrcPort)
		dp = int(udp.DstPort)
		prot = 17
	} else {
		// TCP
		tcpLayer := packet.Layer(layers.LayerTypeTCP)
		if tcpLayer != nil {
			tcp, ok := tcpLayer.(*layers.TCP)
			if !ok {
				return
			}
			sp = int(tcp.SrcPort)
			dp = int(tcp.DstPort)
			if tcp.FIN {
				flag |= 0x01
			}
			if tcp.SYN {
				flag |= 0x02
			}
			if tcp.RST {
				flag |= 0x04
			}
			if tcp.PSH {
				flag |= 0x08
			}
			if tcp.ACK {
				flag |= 0x10
			}
			if tcp.URG {
				flag |= 0x20
			}
			if tcp.ECE {
				flag |= 0x40
			}
			if tcp.CWR {
				flag |= 0x80
			}
			prot = 6
		} else {
			icmpV4Layer := packet.Layer(layers.LayerTypeICMPv4)
			if icmpV4Layer != nil {
				icmp, ok := icmpV4Layer.(*layers.ICMPv4)
				if !ok {
					return
				}
				prot = 1
				dp = int(icmp.TypeCode)
			} else {
				icmpV6Layer := packet.Layer(layers.LayerTypeICMPv6)
				if icmpV6Layer == nil {
					return
				}
				icmp, ok := icmpV6Layer.(*layers.ICMPv6)
				if !ok {
					return
				}
				prot = 58
				dp = int(icmp.TypeCode)
			}
		}
	}
	record["srcIP"] = src
	record["srcPort"] = float64(sp)
	record["dstIP"] = dst
	record["dstPort"] = float64(dp)
	record["bytes"] = float64(bytes)
	record["tcpflags"] = float64(flag)
	record["protocol"] = float64(prot)

	log.Printf("rawPacketFlow : %+v", record)
}

変更したのは


			case *sflow.FlowSample:
				log.Printf("FlowSample from %s", raIP)
				for _, record := range s.Records {
-					log.Printf("%+v", record)
+					switch fsr := record.(type) {
+					case sflow.RawPacketFlow:
+						rawPacketFlow(&fsr)
+					}
				}

の部分とrawPacketFlow()関数の追加です。

rawPacketFlow()関数のポイントは、gopacketを使ってパケットのデコードをする部分です。

sFlowのサンプルのHead部分をデコード

	packet := gopacket.NewPacket(r.Header, layers.LayerTypeEthernet, gopacket.Default)

情報を取得したいレイアーのデコード

    // Ethernet
	ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
    // IPv4
    ipv4Layer := packet.Layer(layers.LayerTypeIPv4)
	// IPv6
    ipv6Layer := packet.Layer(layers.LayerTypeIPv6)

     // UDP
	udpLayer := packet.Layer(layers.LayerTypeUDP)
	
     // TCP
	tcpLayer := packet.Layer(layers.LayerTypeTCP)

実行結果

2024/09/30 16:08:17 FlowSample from 192.168.1.32
2024/09/30 16:08:17 rawPacketFlow : map[bytes:36 destinationMacAddress:00:25:36:ab:77:53 dstIP:ff02::1 dstPort:33280 protocol:58 sourceMacAddress:00:25:36:ab:77:53 srcIP:fe80::225:36ff:feab:7753 srcPort:0 tcpflags:0]
2024/09/30 16:08:19 FlowSample from 192.168.1.32
2024/09/30 16:08:19 rawPacketFlow : map[bytes:88 destinationMacAddress:50:00:00:04:00:00 dstIP:192.168.1.250 dstPort:2055 protocol:17 sourceMacAddress:50:00:00:04:00:00 srcIP:10.30.1.12 srcPort:3285 tcpflags:0]

送信元、宛先のMACアドレスやIPアドレス、ポート番号、プロトコルなどをデコードできています。

余談

gopacketは、

の開発で利用しました。

使い方は

にも書いています。

TWSNMP FCに同梱しています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?