はじめに
前回の記事
の続きです。
前回の記事では、受信した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に同梱しています。