いろいろ備忘録日記

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

Goメモ-317 (go-packetメモ-12)(アプリケーションレイヤーの情報を表示)

関連記事

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のようにパケットを直接見たいときなどに利用できるライブラリに

github.com

pkg.go.dev

というのがあります。

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

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

ちゃんとペイロードが取れていますね。

リポジトリ

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

github.com

参考情報

Goのおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


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

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