首頁  >  文章  >  後端開發  >  使用 Golang 擷取流量

使用 Golang 擷取流量

王林
王林原創
2024-09-04 11:07:29618瀏覽

Capture traffic with Golang

介紹

大多數軟體工程界的人都聽過 TCPDump、WireShark 等
您很可能聽說過 TCPDump 團隊開發的 libpcap1 庫,用於捕獲流量並由 Wireshark 使用。

這個函式庫1提供了一個靈活的介面來捕捉來自網路介面的流量並在程式碼中處理它。它提供了編寫高效能程式碼的靈活性,並包含根據需要收集所需資料的盡可能多的業務邏輯。

最近我發現了一項任務,收集所需的資料包、分析它們並保存以供將來的手動審核是一個好主意。要手動檢查,可以使用 WireShak 載入 pcap 檔案並使用漂亮的 UI 手動檢查收集的資料包。

最重要的部分,為什麼是 Go? Go 是一種足夠簡單的語言,可以編寫程式碼並由多個團隊成員支援。它比 C 和 C++ 安全得多,而且其他人支持它所需的經驗要少得多,而不會出現意外的意外。除非我們沒有要求,必須使用許多高效能語言(C、C++、Rust 等),否則我更喜歡選擇 Golang。

任務定義

在我們做某件事之前,最好先了解一下,我們想要實現什麼結果?讓我們定義一個簡短的要求清單。

要求

為了讓實作盡可能簡單,我們只定義幾個點:

  1. 我們想要收集出站流量
  2. 讓我們收集 IPv4 流量
  3. 忽略專用網路流量
  4. 讓我們保留 UDP 封包

這些簡單的幾點足以了解如何使用 Golang 的 libpcap1
之後,在此基礎上添加什麼邏輯就只是想像的問題了。

執行

在開始之前,我們先定義一下程式碼不適合生產。我們的目標是查看最小的範例並測試它是否運作良好。

我們將使用這些函式庫:

  1. 登入 slog
  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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn