関連記事
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) - いろいろ備忘録日記
Goメモ-310 (go-packetメモ-05)(*pcap.Packetの中身を表示) - いろいろ備忘録日記
Goメモ-311 (go-packetメモ-06)(*layers.Ethernetの情報を表示) - いろいろ備忘録日記
Goメモ-312 (go-packetメモ-07)(*layers.ARPの情報を表示) - いろいろ備忘録日記
Goメモ-313 (go-packetメモ-08)(*layers.ICMPv4の情報を表示) - いろいろ備忘録日記
Goメモ-314 (go-packetメモ-09)(*layers.IPv4の情報を表示) - いろいろ備忘録日記
Goメモ-315 (go-packetメモ-10)(*layers.TCPの情報を表示) - いろいろ備忘録日記
Goメモ-316 (go-packetメモ-11)(*layers.UDPの情報を表示) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。忘れないうちにメモメモ。。。
GoでWireSharkやtcpdumpのようにパケットを直接見たいときなどに利用できるライブラリに
というのがあります。
今まで使ったこと無かったのですが、使うと面白かったので自分用のメモ代わりにちょこちょこ残しておこうと思います。
Linux (Ubuntu) 上で遊んでいますので、Windowsの場合はWinPcap (WireSharkをインストールするときについでにインストールできたはず)が必要になると思います。
インストール
libpcap
が必要ですので、以下でインストールします。
$ sudo apt install libpcap-dev
あと、tcpdumpが入っていない場合は以下もついでに入れておきます。(これはオプショナルです)
$ sudo apt install tcpdump
試してみる
使い方に関しては、上に挙げている go-packet の godoc の方に詳しく書かれています。
今回は、アプリケーションレイヤーの中身を見てみます。
アプリケーションレイヤーの取得方法は、2通りあって
appLayer := p.ApplicationLayer() if appLayer == nil { return }
という形でパケットから直接取得するか、特定の下位レイヤーから
tcpLayer := p.Layer(layers.LayerTypeTCP) if tcpLayer == nil { return } tcp := tcpLayer.(*layers.TCP) // ペイロード取得 var payload []byte payload = tcp.Payload
のように取得することも可能。
main.go
// Package main is the example of gopacket.Packet.ApplicationLayer() package main import ( "fmt" "os" "os/signal" "github.com/devlights/gomy/logops" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" ) var ( appLog, errLog, _ = logops.Default.Logger(false) ) func main() { if err := run(); err != nil { errLog.Panic(err) } } func run() error { const ( pcapfile = "example.pcap" filter = "" ) defer func() { appLog.Println("DONE") }() // -------------------------------------- // Open capture handle // -------------------------------------- var ( handle *pcap.Handle err error ) handle, err = pcap.OpenOffline(pcapfile) 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 ( doneCh = make(chan struct{}) 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() ) appLog.Println("START") LOOP: for { select { case <-sigCh: close(doneCh) break LOOP case p, ok := <-packetCh: if !ok { break LOOP } see(p) } } return nil } func see(p gopacket.Packet) { // // 以下、どちらも同じことをしている // // gopacket.Packet.ApplicationLayer() で、アプリケーション層のペイロードを取得 appLayer := p.ApplicationLayer() if appLayer == nil { return } appLog.Printf("[ApplicatonLayer][Payload ] %v bytes", len(appLayer.Payload())) appLog.Printf("[ApplicatonLayer][Contents] %v", string(appLayer.Payload())) // TCPレイヤーからペイロードを取得 tcpLayer := p.Layer(layers.LayerTypeTCP) if tcpLayer == nil { return } tcp := tcpLayer.(*layers.TCP) appLog.Printf("[TCP Layer ][Payload ] %v bytes", len(tcp.Payload)) appLog.Printf("[TCP Layer ][Contents] %v", string(tcp.Payload)) appLog.Println("----------------") }
Taskfile.yml
タスク定義は以下のような感じ。
layertype-app: desc: See gopacket.Packet.ApplicationLayer() info dir: cmd/layertype/app cmds: - go build - sudo bash ./tcpdump.sh - sudo bash ./server.sh - sleep 1 - sudo bash ./client.sh - sleep 3 - sudo bash ./kill.sh - sleep 1 - sudo ./app
server.sh
#!/usr/bin/env bash nc -l -k 127.0.0.1 22222 1>/dev/null & echo -n $! > server.pid exit 0
client.sh
#!/usr/bin/env bash echo -n helloworld > .tmp nc -N 127.0.0.1 22222 < .tmp echo -n golang > .tmp nc -N 127.0.0.1 22222 < .tmp echo -n goroutine > .tmp nc -N 127.0.0.1 22222 < .tmp exit 0
以下、Gitpod上で実行してみた結果です。
gitpod /workspace/go-gopacket-example (main) $ task layertype-app task: [layertype-app] go build task: [layertype-app] sudo bash ./tcpdump.sh task: [layertype-app] sudo bash ./server.sh task: [layertype-app] sleep 1 task: [layertype-app] sudo bash ./client.sh task: [layertype-app] sleep 3 task: [layertype-app] sudo bash ./kill.sh task: [layertype-app] sleep 1 task: [layertype-app] sudo ./app START [ApplicatonLayer][Payload ] 10 bytes [ApplicatonLayer][Contents] helloworld [TCP Layer ][Payload ] 10 bytes [TCP Layer ][Contents] helloworld ---------------- [ApplicatonLayer][Payload ] 6 bytes [ApplicatonLayer][Contents] golang [TCP Layer ][Payload ] 6 bytes [TCP Layer ][Contents] golang ---------------- [ApplicatonLayer][Payload ] 9 bytes [ApplicatonLayer][Contents] goroutine [TCP Layer ][Payload ] 9 bytes [TCP Layer ][Contents] goroutine ---------------- DONE
ちゃんとペイロードが取れていますね。
リポジトリ
上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。