関連記事
Goメモ-306 (go-packetメモ-01)(ネットワークインターフェースを表示) - いろいろ備忘録日記
Goメモ-307 (go-packetメモ-02)(流れるパケットをキャプチャする)(OpenLive) - いろいろ備忘録日記
Goメモ-308 (go-packetメモ-03)(pcapファイルを読み込み)(OpenOffline) - いろいろ備忘録日記
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 の方に詳しく書かれています。
今回は、BPF (Berkeley Packet Filter) フィルタを設定する方法についてです。
pcap.SetBPFFilter
関数を使います。
main.go
// Package main is the example of go-packet with BPF (Berkeley Packet Filter) filter package main import ( "fmt" "log" "os" "os/signal" "github.com/google/gopacket" "github.com/google/gopacket/pcap" ) var ( appLog = log.New(os.Stderr, "", 0) ) func main() { if err := run(); err != nil { panic(err) } } func run() error { const ( pcapfile = "example.pcap" filter = "icmp or icmp6" ) 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 // // # filter examples: // - ip src 192.168.1.1 // - ip dst 192.168.1.2 // - ip host 192.168.1.1 and ip host 192.168.1.2 // - tcp port 80 // - udp port 53 // - icmp or icmp6 // - ether src aa:bb:cc:dd:ee:ff // - ether dst aa:bb:cc:dd:ee:ff // - vlan 100 // - ip host 192.168.1.1 and tcp port 80 // -------------------------------------- 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() ) appLog.Println("START") LOOP: for { select { case <-sigCh: break LOOP case p, ok := <-packetCh: if !ok { break LOOP } appLog.Println(p) } } return nil }
tcpdump.sh
#!/usr/bin/env bash tcpdump -t -n -i lo -w example.pcap & echo $! > tcpdump.pid exit 0
ping.sh
#!/usr/bin/env bash timeout 3s ping localhost exit 0
kill.sh
#!/usr/bin/env bash kill $(cat tcpdump.pid) exit 0
以下、Gitpod上で実行してみた結果です。
gitpod /workspace/go-gopacket-example (main) $ task bpffilter task: [bpffilter] go build task: [bpffilter] sudo bash ./tcpdump.sh task: [bpffilter] bash ./ping.sh PING localhost(localhost (::1)) 56 data bytes 64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.014 ms tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes 64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.034 ms 64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.039 ms task: [bpffilter] sudo bash ./kill.sh 33 packets captured 66 packets received by filter 0 packets dropped by kernel task: [bpffilter] sleep 1 task: [bpffilter] sudo ./bpffilter START PACKET: 118 bytes, wire length 118 cap length 118 @ 2023-04-18 02:07:43.05961 +0000 UTC - Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..104..] SrcMAC=00:00:00:00:00:00 DstMAC=00:00:00:00:00:00 EthernetType=IPv6 Length=0} - Layer 2 (40 bytes) = IPv6 {Contents=[..40..] Payload=[..64..] Version=6 TrafficClass=0 FlowLabel=53830 Length=64 NextHeader=ICMPv6 HopLimit=64 SrcIP=::1 DstIP=::1 HopByHop=nil} - Layer 3 (04 bytes) = ICMPv6 {Contents=[128, 0, 32, 129] Payload=[..60..] TypeCode=EchoRequest Checksum=8321 TypeBytes=[]} - Layer 4 (00 bytes) = ICMPv6Echo {Contents=[] Payload=[] Identifier=12517 SeqNumber=2} PACKET: 118 bytes, wire length 118 cap length 118 @ 2023-04-18 02:07:43.059619 +0000 UTC - Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..104..] SrcMAC=00:00:00:00:00:00 DstMAC=00:00:00:00:00:00 EthernetType=IPv6 Length=0} - Layer 2 (40 bytes) = IPv6 {Contents=[..40..] Payload=[..64..] Version=6 TrafficClass=0 FlowLabel=275845 Length=64 NextHeader=ICMPv6 HopLimit=64 SrcIP=::1 DstIP=::1 HopByHop=nil} - Layer 3 (04 bytes) = ICMPv6 {Contents=[129, 0, 31, 129] Payload=[..60..] TypeCode=EchoReply Checksum=8065 TypeBytes=[]} - Layer 4 (00 bytes) = ICMPv6Echo {Contents=[] Payload=[] Identifier=12517 SeqNumber=2} PACKET: 118 bytes, wire length 118 cap length 118 @ 2023-04-18 02:07:44.07939 +0000 UTC - Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..104..] SrcMAC=00:00:00:00:00:00 DstMAC=00:00:00:00:00:00 EthernetType=IPv6 Length=0} - Layer 2 (40 bytes) = IPv6 {Contents=[..40..] Payload=[..64..] Version=6 TrafficClass=0 FlowLabel=53830 Length=64 NextHeader=ICMPv6 HopLimit=64 SrcIP=::1 DstIP=::1 HopByHop=nil} - Layer 3 (04 bytes) = ICMPv6 {Contents=[128, 0, 222, 50] Payload=[..60..] TypeCode=EchoRequest Checksum=56882 TypeBytes=[]} - Layer 4 (00 bytes) = ICMPv6Echo {Contents=[] Payload=[] Identifier=12517 SeqNumber=3} PACKET: 118 bytes, wire length 118 cap length 118 @ 2023-04-18 02:07:44.079401 +0000 UTC - Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..104..] SrcMAC=00:00:00:00:00:00 DstMAC=00:00:00:00:00:00 EthernetType=IPv6 Length=0} - Layer 2 (40 bytes) = IPv6 {Contents=[..40..] Payload=[..64..] Version=6 TrafficClass=0 FlowLabel=275845 Length=64 NextHeader=ICMPv6 HopLimit=64 SrcIP=::1 DstIP=::1 HopByHop=nil} - Layer 3 (04 bytes) = ICMPv6 {Contents=[129, 0, 221, 50] Payload=[..60..] TypeCode=EchoReply Checksum=56626 TypeBytes=[]} - Layer 4 (00 bytes) = ICMPv6Echo {Contents=[] Payload=[] Identifier=12517 SeqNumber=3} DONE
実行すると、最初に3秒間ほど tcpdump コマンドを実行して ping (ICMP エコーメッセージ) をキャプチャして、pcapファイルを出力します。
その後、それをgo-packetを使って読み込んでパケットをBPFフィルタ設定して表示しています。
pingコマンドを実行する際に -4
オプションを付けていないので、ICMPv6 でメッセージが送受信されています。
なので、上のソースのBPFフィルタを icmp or icmp6
から icmp
にして、実行すると何も表示されなくなります。
ちゃんとフィルタリングされていますね。
リポジトリ
上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。