使用 Golang 捕获流量

使用 golang 捕获流量

介绍

大多数软件工程界的人都听说过 tc++pdump、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 捕获流量的详细内容,更多请关注其它相关文章!