いろいろ備忘録日記

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

Goメモ-314 (go-packetメモ-09)(*layers.IPv4の情報を表示)

関連記事

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の情報を表示) - いろいろ備忘録日記

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 の方に詳しく書かれています。

今回は、実際のインターネットプロトコルレイヤーを表す *layers.IPv4 の中身を見てみます。

最もよく利用されているネットワーク層のプロトコルですね。

(OSI参照モデルではネットワーク層、TCP/IPプロトコル群ではインターネット層)

各レイヤーの取得方法は、大抵同じやり方になってて、以下のようになります。

   var p gopacket.Packet

    XXXXLayer := p.Layer(layers.LayerTypeXXXX)
    if XXXXLayer != nil {
        xxxx := XXXXLayer.(*layers.XXXX)
    }

XXXXの部分を変えればオッケイ。

main.go

// Package main is the example of *layers.IPv4
package main

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

   "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      = "eth0"
        filter      = "tcp"
        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
            }

            // Display only the first items
            if see(p) {
                break LOOP
            }
        }
    }

    return nil
}

func see(p gopacket.Packet) bool {
    ipv4Layer := p.Layer(layers.LayerTypeIPv4)
    if ipv4Layer == nil {
        return false
    }

    ipv4 := ipv4Layer.(*layers.IPv4)

    appLog.Printf("[Version       ] %v", ipv4.Version)

    // Internet Header Length (IHL)
    //   IPv4ヘッダの長さを32ビットワード単位で示す.
    //   IHLは通常、最小値の55 * 32ビット = 160ビット = 20バイト)であるが
    //   IPヘッダにオプションが含まれている場合、それより大きくなる.
    //
    //   IHLが5の場合、IPv4ヘッダは20バイトの長さを持つ.
    //   IHLが6の場合、IPv4ヘッダは24バイトの長さを持ち、そのうち4バイトがオプションフィールドに割り当てられる.
    //   IHLを使用して、IPヘッダの終わりとペイロード(データ)の開始を正確に判断することができる.
    appLog.Printf("[IHL           ] %v words -> %v bits -> %v bytes", ipv4.IHL, ipv4.IHL*32, ipv4.IHL*32/8)

    // Lengthは IPv4 パケット全体での長さを表す
    appLog.Printf("[Length        ] %v", ipv4.Length)
    appLog.Printf("[Payload Length] %v", len(ipv4.Payload))

    appLog.Printf("[TTL           ] %v", ipv4.TTL)
    appLog.Printf("[Protocol      ] %v", ipv4.Protocol)
    appLog.Printf("[Src IP        ] %v", ipv4.SrcIP)
    appLog.Printf("[Dst IP        ] %v", ipv4.DstIP)

    return true
}

Taskfile.yml

タスク定義は以下のような感じ。

  layertype-ipv4:
    desc: See *layers.IPv4 info
    dir: cmd/layertype/ipv4
    cmds:
      - go build
      - sudo ./ipv4

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

gitpod /workspace/go-gopacket-example (main) $ task layertype-ipv4
task: [layertype-ipv4] go build
task: [layertype-ipv4] sudo ./ipv4
START
[Version       ] 4
[IHL           ] 5 words -> 160 bits -> 20 bytes
[Length        ] 120
[Payload Length] 100
[TTL           ] 111
[Protocol      ] TCP
[Src IP        ] xxx.xxx.xxx.xxx
[Dst IP        ] xxx.xxx.xxx.xxx
DONE

実行すると、流れるIPv4パケットの情報が表示されます。

リポジトリ

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

github.com

参考情報

Goのおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


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

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