ホームページ  >  記事  >  バックエンド開発  >  Golang でトラフィックをキャプチャする

Golang でトラフィックをキャプチャする

王林
王林オリジナル
2024-09-04 11:07:29666ブラウズ

Capture traffic with Golang

導入

ソフトウェア エンジニアリングの世界の人々のほとんどは、TCPDump、WireShark などについて聞いたことがあるでしょう。
そしておそらく、トラフィックをキャプチャするために TCPDump チームによって開発され、Wireshark によっても使用される libpcap1 ライブラリについて聞いたことがあるでしょう。

このライブラリ1は、ネットワーク インターフェイスからトラフィックをキャプチャし、コードで処理するための柔軟なインターフェイスを提供します。これにより、パフォーマンスの高いコードを記述し、必要なデータのみを収集するために必要なだけのビジネス ロジックを組み込むことができる柔軟性が得られます。

最近、必要なパケットを収集して分析し、将来の手動レビューに備えて保存することが良いタスクであると判断しました。手動でレビューするには、WireShak を使用して pcap ファイルをロードし、優れた UI を使用して収集されたパケットを手動でレビューできます。

そして最も重要な部分は、なぜ Go なのかということです。 Go は、コードを作成し、複数のチーム メンバーがそれをサポートできるほどシンプルで優れた言語です。これは C や C++ よりもはるかに安全であり、他の人が予期せぬことをせずにサポートできるようになるには、はるかに少ない経験しか必要ありません。多くのパフォーマンス言語 (C、C++、Rust など) を使用する必要がある要件がなくなるまでは、Golang を選択することを好みます。

タスクの定義

何かをする前に、結果として何を達成したいのかを理解しておくとよいでしょう。要件の短いリストを定義しましょう。

要件

実装をできるだけ単純にするために、いくつかの点だけを定義しましょう:

  1. 発信トラフィックを収集したい
  2. IPv4 トラフィックを収集しましょう
  3. プライベートネットワークトラフィックを無視します
  4. そして、UDP パケットを保存しましょう

これらの簡単ないくつかのポイントは、Golang から libpcap1 を使用する方法を理解するのに十分です。
その後、その上にどのようなロジックが追加されるかは想像力の問題です。

実装

始める前に、コードが本番環境に対応しているとは想定されていないことを定義しましょう。私たちの目標は、最小限の例を確認し、それがうまく機能することをテストすることです。

次のライブラリを使用します:

  1. ログイン用のスログ
  2. github.com/google/gopacket パケットのキャプチャと pcap ファイル形式のフォーマット

システムのインターフェース名は異なる可能性が高くなりますが、将来のコードのインターフェースは eth0 になります。

実用的なコード

これは、コピーして自分の環境で試すことができるコメント付きのコードです。
libpcap1 を使用するため、アプリケーションは CGO を使用し、root ユーザーからアプリを実行する必要があります。

package main

import (
    "bytes"
    "log/slog"
    "net"
    "os"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
    "github.com/google/gopacket/pcapgo"
)

const (
    interfaceName = "eth0"
    snaplen       = 1500
)

func main() {
    slog.Info("Running our applicaiton...")

    // Get handler attached to an interface.
    handle, err := pcap.OpenLive(interfaceName, snaplen, true, pcap.BlockForever)
    if err != nil {
        slog.Error("Could not OpenLive", slog.String("err", err.Error()))
        os.Exit(1)
    }

    iface, err := net.InterfaceByName(interfaceName)
    if err != nil {
        slog.Error("Could not OpenLive", slog.String("err", err.Error()))
        os.Exit(1)
    }

    // Start new Source reader.
    source := gopacket.NewPacketSource(handle, handle.LinkType())

    // This is suppose to be a file writer, but we will use memory, just for simplification.
    fileWriter := bytes.NewBuffer(nil)
    pcapWriter := pcapgo.NewWriterNanos(fileWriter)
    err = pcapWriter.WriteFileHeader(snaplen, handle.LinkType())
    if err != nil {
        slog.Error("Could not write pcap header", slog.String("err", err.Error()))
        os.Exit(1)
    }

    // Reading packages.
    for packet := range source.Packets() {
        // Filter by outcoming traffic only.
        // To filter it, we need to compare MAC addresses from out interface and source MAC.
        // To access a mac Address we need to get an Ethernet layer.
        layer := packet.Layer(layers.LayerTypeEthernet)

        ethernet, ok := layer.(*layers.Ethernet)
        if !ok {
            slog.Error("Could not get Ethernet layer")
            continue
        }

        if !bytes.Equal(ethernet.SrcMAC, iface.HardwareAddr) {
            // Our interface did not send this packet. It's not outcoming.
            continue
        }

        // Now we need to identify IPv4 layer.
        layer = packet.Layer(layers.LayerTypeIPv4)

        ipv4, ok := layer.(*layers.IPv4)
        if !ok {
            // It's not IPv4 traffic.
            continue
        }

        if ipv4.DstIP.IsPrivate() {
            // Do not collect private traffic.
            continue
        }

        if ipv4.Protocol != layers.IPProtocolUDP {
            // Ignore not UDP protocol.
            continue
        }

        err = pcapWriter.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
        if err != nil {
            slog.Error("Could not write a packet to a pcap writer", slog.String("err", err.Error()))

            continue
        }

        slog.Info("Stored packet", slog.Any("packet", packet))

        // Let's collect ONLY 100K bytes, just for example perposes.
        if fileWriter.Len() > 100000 {
            break
        }
    }

    slog.Info("We have successfuly collected bytes", slog.Int("bytes", fileWriter.Len()))
}

そこで実行すると、切り詰められた出力は次のようになります:

2024/08/31 13:35:36 INFO Running our applicaiton...
2024/08/31 13:37:48 INFO Stored packet packet="PACKET: 105 bytes, wire length 105 cap length 105 ..."
...
2024/08/31 13:37:48 INFO Stored packet packet="PACKET: 1291 bytes, wire length 1291 cap length 1291 ..."
2024/08/31 13:37:48 INFO We have successfuly collected bytes bytes=101018

この最小限の例が、誰かが Go を使用してこの分野への旅を始めるのに役立つことを願っています。

あとがき

日常業務で新しい要件を満たすが、どうすればよいかわからない場合。周りの人に遠慮せずに、プライベートで私に手紙を書いて、自分で調べてください。

リサーチとコミュニケーションは、すべての問題解決の鍵となります。


  1. libpcap は、ネットワーク トラフィック キャプチャ用のポータブル C/C++ ライブラリです ↩

以上がGolang でトラフィックをキャプチャするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。