いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-307 (go-packetメモ-02)(流れるパケットをキャプチャする)(OpenLive)

関連記事

Goメモ-306 (go-packetメモ-01)(ネットワークインターフェースを表示) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

GoでWireSharkやtcpdumpのようにパケットを直接見たいときなどに利用できるライブラリに

github.com

pkg.go.dev

というのがあります。

今まで使ったこと無かったのですが、使うと面白かったので自分用のメモ代わりにちょこちょこ残しておこうと思います。

Linux (Ubuntu) 上で遊んでいますので、Windowsの場合はWinPcap (WireSharkをインストールするときについでにインストールできたはず)が必要になると思います。

インストール

libpcap が必要ですので、以下でインストールします。

$ sudo apt install libpcap-dev

あと、tcpdumpが入っていない場合は以下もついでに入れておきます。(これはオプショナルです)

$ sudo apt install tcpdump

試してみる

使い方に関しては、上に挙げている go-packet の godoc の方に詳しく書かれています。

今回は、今流れているパケットをキャプチャしてみます。

pcap.OpenLive 関数を使います。

// Package main is the example of pcap.OpenLive()
package main

import (
   "fmt"
   "log"
   "os"
   "os/signal"
   "time"

   "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 (
        device      = "eth0"
        filter      = ""
        snapshotLen = int32(1600)
        promiscuous = false
        timeout     = 1 * time.Second
    )

    defer func() { appLog.Println("DONE") }()

    // --------------------------------------
    // Open capture handle
    // --------------------------------------
    var (
        handle *pcap.Handle
        err    error
    )

    handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
    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()
    )
    appLog.Println("START")

LOOP:
    for {
        select {
        case <-sigCh:
            break LOOP
        case p, ok := <-packetCh:
            if !ok {
                break LOOP
            }

            appLog.Println(p)
        }
    }

    return nil
}

以下、Gitpod上で実行してみた結果です。

gitpod /workspace/go-gopacket-example (main) $ task openlive
task: [openlive] go build
task: [openlive] sudo ./openlive
START
PACKET: 92 bytes, wire length 92 cap length 92 @ 2023-04-10 23:28:25.323176 +0000 UTC
- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..78..] SrcMAC=c6:fa:60:01:56:7f DstMAC=ce:39:d9:53:80:6c EthernetType=IPv4 Length=0}
- Layer 2 (20 bytes) = IPv4     {Contents=[..20..] Payload=[..58..] Version=4 IHL=5 TOS=0 Length=78 Id=31324 Flags=DF FragOffset=0 TTL=64 Protocol=TCP Checksum=24663 SrcIP=10.0.5.2 DstIP=192.168.144.76 Options=[] Padding=[]}
- Layer 3 (32 bytes) = TCP      {Contents=[..32..] Payload=[..26..] SrcPort=23000(inovaport1) DstPort=47282 Seq=2748083048 Ack=3081863157 DataOffset=8 FIN=false SYN=false RST=false PSH=true ACK=true URG=false ECE=false CWR=false NS=false Window=3674 Checksum=24631 Urgent=0 Options=[TCPOption(NOP:), TCPOption(NOP:), TCPOption(Timestamps:3131574629/3872003262 0xbaa80965e6ca14be)] Padding=[]}
- Layer 4 (26 bytes) = Payload  26 byte(s)

PACKET: 66 bytes, wire length 66 cap length 66 @ 2023-04-10 23:28:25.323983 +0000 UTC
- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..52..] SrcMAC=ce:39:d9:53:80:6c DstMAC=c6:fa:60:01:56:7f EthernetType=IPv4 Length=0}
- Layer 2 (20 bytes) = IPv4     {Contents=[..20..] Payload=[..32..] Version=4 IHL=5 TOS=0 Length=52 Id=49813 Flags=DF FragOffset=0 TTL=61 Protocol=TCP Checksum=6968 SrcIP=192.168.144.76 DstIP=10.0.5.2 Options=[] Padding=[]}
- Layer 3 (32 bytes) = TCP      {Contents=[..32..] Payload=[] SrcPort=47282 DstPort=23000(inovaport1) Seq=3081863157 Ack=2748083074 DataOffset=8 FIN=false SYN=false RST=false PSH=false ACK=true URG=false ECE=false CWR=false NS=false Window=331 Checksum=64571 Urgent=0 Options=[TCPOption(NOP:), TCPOption(NOP:), TCPOption(Timestamps:3872003302/3131574629 0xe6ca14e6baa80965)] Padding=[]}

[パケットキャプチャが沢山表示されます]

^Ctask: Signal received: "interrupt"
DONE

実行するとドワーっと表示されます。Ctrl-Cで止めます。実行するには root権限 が必要なので sudo して実行します。

上のプログラムの filter 変数に BPFフィルターを設定すると表示するパケットを絞り込むことが出来ます。

リポジトリ

上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。

github.com

参考情報

Goのおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。