関連記事
Goメモ-306 (go-packetメモ-01)(ネットワークインターフェースを表示) - いろいろ備忘録日記
Goメモ-307 (go-packetメモ-02)(流れるパケットをキャプチャする)(OpenLive) - いろいろ備忘録日記
Goメモ-308 (go-packetメモ-03)(pcapファイルを読み込み)(OpenOffline) - いろいろ備忘録日記
Goメモ-309 (go-packetメモ-04)(BPFフィルタを設定)(SetBPFFilter) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。忘れないうちにメモメモ。。。
GoでWireSharkやtcpdumpのようにパケットを直接見たいときなどに利用できるライブラリに
というのがあります。
今まで使ったこと無かったのですが、使うと面白かったので自分用のメモ代わりにちょこちょこ残しておこうと思います。
Linux (Ubuntu) 上で遊んでいますので、Windowsの場合はWinPcap (WireSharkをインストールするときについでにインストールできたはず)が必要になると思います。
インストール
libpcap
が必要ですので、以下でインストールします。
$ sudo apt install libpcap-dev
あと、tcpdumpが入っていない場合は以下もついでに入れておきます。(これはオプショナルです)
$ sudo apt install tcpdump
また、今回のサンプルでは nc (netcat) を使っていますので、以下もある方が良いです。
$ sudo apt install netcat
試してみる
使い方に関しては、上に挙げている go-packet の godoc の方に詳しく書かれています。
今回は、実際のパケットを表す *pcap.Packet
の中身を見てみます。
main.go
// Package main is the example of gopacket.Packet package main import ( "fmt" "log" "os" "os/signal" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" ) var ( appLog = log.New(os.Stderr, "", 0) ) func main() { if err := run(); err != nil { appLog.Panic(err) } } func run() error { const ( device = "lo" filter = "tcp port 22222" snapshotLen = int32(128) promiscuous = false ) // -------------------------------------- // Open capture handle // -------------------------------------- var ( handle *pcap.Handle err error ) handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, pcap.BlockForever) if err != nil { return fmt.Errorf("error open handle: %w", err) } defer handle.Close() // -------------------------------------- // Apply capture filter (optional) // -------------------------------------- if filter != "" { err = handle.SetBPFFilter(filter) if err != nil { return fmt.Errorf("error apply filter: %w", err) } } // -------------------------------------- // Set signal handler // -------------------------------------- var ( sigCh = make(chan os.Signal, 1) ) signal.Notify(sigCh, os.Interrupt) // -------------------------------------- // Make packet source and display. // -------------------------------------- var ( dataSource gopacket.PacketDataSource = handle decoder gopacket.Decoder = handle.LinkType() packetSource *gopacket.PacketSource = gopacket.NewPacketSource(dataSource, decoder) packetCh <-chan gopacket.Packet = packetSource.Packets() ) LOOP: for { select { case <-sigCh: break LOOP case p, ok := <-packetCh: if !ok { break LOOP } see(p) } } return nil } func see(p gopacket.Packet) { // ダンプ出力(各レイヤー毎の詳細も見れる) //appLog.Printf("[Dump] %v", p.Dump()) // データ (各レイヤー毎のフルパケットデータが見れる) //appLog.Printf("[Data] %v", p.Data()) appLog.Println("------------------------------") { appLog.Printf("[Capture Length] %v", p.Metadata().CaptureLength) ipLayer := p.Layer(layers.LayerTypeIPv4) if ipLayer != nil { ipv4, ok := ipLayer.(*layers.IPv4) if ok { appLog.Printf("[Src ] %v", ipv4.SrcIP) appLog.Printf("[Dst ] %v", ipv4.DstIP) appLog.Printf("[Protocol ] %v", ipv4.Protocol) } } tcpLayer := p.Layer(layers.LayerTypeTCP) if tcpLayer != nil { tcp, ok := tcpLayer.(*layers.TCP) if ok { appLog.Printf("[SRC PORT ] %v", tcp.SrcPort) appLog.Printf("[DST PORT ] %v", tcp.DstPort) appLog.Println("[TCP FLAGS ]") appLog.Printf(">>> SYN=%v", tcp.SYN) appLog.Printf(">>> ACK=%v", tcp.ACK) appLog.Printf(">>> PSH=%v", tcp.PSH) appLog.Printf(">>> RST=%v", tcp.RST) appLog.Printf(">>> FIN=%v", tcp.FIN) } } appLayer := p.ApplicationLayer() if appLayer != nil { payload := appLayer.Payload() if payload != nil { appLog.Printf("[Payload ] %v", payload) } } } appLog.Println("------------------------------") }
app.sh
#!/usr/bin/env bash ./packet & echo -n $! > app.pid exit 0
server.sh
#!/usr/bin/env bash nc -l -k 127.0.0.1 22222 & echo -n $! > server.pid exit 0
client.sh
#!/usr/bin/env bash echo -n helloworld > .tmp nc -N 127.0.0.1 22222 < .tmp exit 0
kill.sh
#!/usr/bin/env bash kill $(cat ./app.pid) kill $(cat ./server.pid)
以下、Gitpod上で実行してみた結果です。
gitpod /workspace/go-gopacket-example (main) $ task packet task: [packet] go build task: [packet] sudo bash ./app.sh task: [packet] sleep 1 task: [packet] sudo bash ./server.sh task: [packet] sudo bash ./client.sh helloworldtask: [packet] sleep 3 ------------------------------ [Capture Length] 74 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 53850 [DST PORT ] 22222(easyengine) [TCP FLAGS ] >>> SYN=true >>> ACK=false >>> PSH=false >>> RST=false >>> FIN=false ------------------------------ ------------------------------ [Capture Length] 74 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 22222(easyengine) [DST PORT ] 53850 [TCP FLAGS ] >>> SYN=true >>> ACK=true >>> PSH=false >>> RST=false >>> FIN=false ------------------------------ ------------------------------ [Capture Length] 66 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 53850 [DST PORT ] 22222(easyengine) [TCP FLAGS ] >>> SYN=false >>> ACK=true >>> PSH=false >>> RST=false >>> FIN=false ------------------------------ ------------------------------ [Capture Length] 76 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 53850 [DST PORT ] 22222(easyengine) [TCP FLAGS ] >>> SYN=false >>> ACK=true >>> PSH=true >>> RST=false >>> FIN=false [Payload ] [104 101 108 108 111 119 111 114 108 100] ------------------------------ ------------------------------ [Capture Length] 66 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 22222(easyengine) [DST PORT ] 53850 [TCP FLAGS ] >>> SYN=false >>> ACK=true >>> PSH=false >>> RST=false >>> FIN=false ------------------------------ ------------------------------ [Capture Length] 66 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 53850 [DST PORT ] 22222(easyengine) [TCP FLAGS ] >>> SYN=false >>> ACK=true >>> PSH=false >>> RST=false >>> FIN=true ------------------------------ ------------------------------ [Capture Length] 66 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 22222(easyengine) [DST PORT ] 53850 [TCP FLAGS ] >>> SYN=false >>> ACK=true >>> PSH=false >>> RST=false >>> FIN=true ------------------------------ ------------------------------ [Capture Length] 66 [Src ] 127.0.0.1 [Dst ] 127.0.0.1 [Protocol ] TCP [SRC PORT ] 53850 [DST PORT ] 22222(easyengine) [TCP FLAGS ] >>> SYN=false >>> ACK=true >>> PSH=false >>> RST=false >>> FIN=false ------------------------------ task: [packet] sudo bash ./kill.sh
実行すると、ダミーのサーバを 22222 ポートに立てて、そこに通信しています。
それをキャプチャして表示しています。
パケットの情報を表示してみると、ちゃんとTCPヘッダのフラグとかを確認できますね。
SYN,ACKとかPSH, FINとかが見えます。
リポジトリ
上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。