fako1024/slimcap
GitHub: fako1024/slimcap
一个纯 Go 的高性能网络数据包捕获库,通过零拷贝和 AF_PACKET ring buffer 在 Linux 上实现极低资源占用的流量抓包。
Stars: 14 | Forks: 1
# 一个高性能的网络数据包捕获库
[](https://github.com/fako1024/slimcap/releases)
[](https://godoc.org/github.com/fako1024/slimcap/)
[](https://goreportcard.com/report/github.com/fako1024/slimcap)
[](https://github.com/fako1024/slimcap/actions?query=workflow%3AGo)
[](https://github.com/fako1024/slimcap/actions/workflows/codeql.yml)
本包提供了一个简单而强大的接口,用于执行网络数据包捕获/嗅探。它专注于高性能/高流量吞吐量。
## 功能特性
- 支持通过 AF_PACKET (Linux) 直接从网络 socket 或使用 ring buffer 进行原始负载/IP 层数据包捕获
- 极低的 CPU 使用率和内存(分配)占用,支持零拷贝操作
- 虚拟/模拟捕获源,包括从 PCAP 文件进行流量重放(甚至可以“链式”组合多个源)
- 内置对数据包类型/方向检测的支持
- 使用原生 Go 编写(无 `CGO` 依赖)
## 安装说明
```
go get -u github.com/fako1024/slimcap
```
## 用法
#### **使用 `capture.Source` 接口的各种选项在网络接口上捕获少量数据包,使用固定的捕获长度(即 snaplen)为 64 字节:**
```
listener, err := afpacket.NewSource("enp1s0",
afpacket.CaptureLength(link.CaptureLengthFixed(64)),
)
if err != nil {
// Error handling
}
// Capture a packet from the wire (allocate & copy)
p, err := listener.NextPacket(nil)
if err != nil {
// Error handling
}
fmt.Printf("Received packet on enp1s0 (total len %d): %v (inbound: %v)\n",
p.TotalLen(), p.Payload(), p.IsInbound())
// Capture a packet from the wire (copy to existing / reusable buffer packet)
pBuf := listener.NewPacket()
p, err := listener.NextPacket(pBuf)
if err != nil {
// Error handling
}
fmt.Printf("Received packet on enp1s0 (total len %d): %v (inbound: %v)\n",
p.TotalLen(), p.Payload(), p.IsInbound())
// Capture a packet from the wire (function execution)
if err := listener.NextPacketFn(func(payload []byte, totalLen uint32, pktType, ipLayerOffset byte) (err error) {
fmt.Printf("Received packet on enp1s0 (total len %d): %v (inbound: %v)\n",
totalLen, payload, pktType != capture.PacketOutgoing)
return
}); err != nil {
// Error handling
}
// Close the listener / the capture
if err := listener.Close(); err != nil {
// Error handling
}
```
#### **使用 `capture.SourceZeroCopy` 接口的各种选项在网络接口上执行零拷贝捕获少量数据包,使用最优捕获长度(即 snaplen)以确保可以访问 IPv4 和 IPv6 数据包的任何传输层,并设置自定义的 ring buffer 大小/块数量:**
```
listener, err := afring.NewSource("enp1s0",
afring.CaptureLength(link.CaptureLengthMinimalIPv6Transport),
afring.BufferSize((1<<20), 4),
afring.Promiscuous(false),
)
if err != nil {
// Error handling
}
// Capture a raw packet (full payload) from the wire (zero-copy, no heap allocation)
payload, pktType, totalLen, err := listener.NextPayloadZeroCopy()
if err != nil {
// Error handling
}
fmt.Printf("Received payload on enp1s0 (total len %d): %v (inbound: %v)\n",
totalLen, payload, pktType != capture.PacketOutgoing)
// Capture a packet (IP layer only) from the wire (zero-copy, no heap allocation)
ipLayer, pktType, totalLen, err := listener.NextIPPacketZeroCopy()
if err != nil {
// Error handling
}
fmt.Printf("Received IP layer on enp1s0 (total len %d): %v (inbound: %v)\n",
totalLen, ipLayer, pktType != capture.PacketOutgoing)
// Close the listener / the capture
if err := listener.Close(); err != nil {
// Error handling
}
```
有关更多示例,请参考 [examples](./examples) 中的实现。一个使用 `slimcap` 并展示其所有功能(包括使用模拟源进行端到端测试)的生产级项目是 [goProbe](https://github.com/els0r/goProbe)。
## 性能表现
以下基准测试(参见 [afring_mock_test.go](./capture/afpacket/afring/afring_mock_test.go))展示了单次数据包检索在
整体性能和内存分配占用方面的相对差异(在使用模拟捕获源的标准笔记本电脑上获得)。由于显而易见的原因,零拷贝模式表现最佳,因此应在高吞吐量场景中选用:
```
goarch: amd64
pkg: github.com/fako1024/slimcap/capture/afpacket/afring
cpu: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
BenchmarkCaptureMethods
BenchmarkCaptureMethods/NextPacket 226401712 74.60 ns/op 64 B/op 1 allocs/op
BenchmarkCaptureMethods/NextPacketInPlace 353661006 32.17 ns/op 0 B/op 0 allocs/op
BenchmarkCaptureMethods/NextPayload 176332635 63.81 ns/op 48 B/op 1 allocs/op
BenchmarkCaptureMethods/NextPayloadInPlace 516911223 21.65 ns/op 0 B/op 0 allocs/op
BenchmarkCaptureMethods/NextPayloadZeroCopy 535092314 19.67 ns/op 0 B/op 0 allocs/op
BenchmarkCaptureMethods/NextIPPacket 179753388 64.18 ns/op 48 B/op 1 allocs/op
BenchmarkCaptureMethods/NextIPPacketInPlace 381187490 28.33 ns/op 0 B/op 0 allocs/op
BenchmarkCaptureMethods/NextIPPacketZeroCopy 567278034 19.67 ns/op 0 B/op 0 allocs/op
BenchmarkCaptureMethods/NextPacketFn 559334258 20.44 ns/op 0 B/op 0 allocs/op
```
## 捕获原始数据包
默认情况下,`slimcap` 会根据链路类型自动定义 [BPF](https://docs.kernel.org/bpf/) 过滤器,将捕获的数据包限制为仅包含 IP 层的包。这样做是为了最大化性能并最小化 CPU/内存使用。如果需要捕获原始数据包(即捕获所有数据包而不管其负载内容),可以在创建新的捕获源时禁用自动 BPF 过滤器生成,例如:
```
listener, err := afring.NewSource(devName,
afring.CaptureLength(filter.CaptureLengthFixed(64)),
afring.BufferSize((1<<20), 4),
afring.Promiscuous(false),
afring.IgnoreVLANs(ignoreVLANs),
afring.DisableAutoBPF(), // Disable automatic BPF filter generation
)
```
## 捕获模拟 / 测试
为了在无需依赖_真实_网络接口捕获的情况下支持广泛的测试(直至端到端级别),普通的 AF_PACKET 和 ring buffer 捕获均提供了实现/封装其底层实际实现的模拟层源。因此,只需将任何对 `afpacket` 或 `afring` 源的调用替换为其对应的模拟版本即可,例如将:
```
listener, err := afring.NewSource("enp1s0",
// Options
)
```
更改为
```
listener, err := afring.NewMockSource("enp1s0",
// Options
)
```
通过生成合成的数据包数据或从 PCAP 文件输入先前捕获的数据包,这些模拟源可以像实际的捕获源一样使用(例如用于测试目的)。关于如何使用模拟进行测试的一些好例子可以在 [afpacket_mock_test.go](./capture/afpacket/afpacket/afpacket_mock_test.go) 和 [afring_mock_test.go](./capture/afpacket/afring/afring_mock_test.go) 中找到。
由于模拟实现会产生较小但不可忽视的性能开销(即使在未使用时),`slimcap` 支持一个构建标签(build tag),允许完全禁用模拟(这反过来也会消除上述开销):
```
go build -tags slimcap_nomock
```
建议在要求高性能的生产环境中使用此构建标签。
## Bug 报告 & 功能请求
请使用 [issue tracker](https://github.com/fako1024/slimcap/issues) 提交 bug 和功能请求(或任何其他事项)。
## 许可证
有关使用条件,请参见 [LICENSE](./LICENSE) 文件。
标签:AF_PACKET, EVTX分析, Golang, Libpcap替代, Linux网络, PCAP, Sniffer, 原生网络编程, 安全编程, 开发库, 抓包库, 日志审计, 流量回放, 目录遍历, 网络嗅探, 网络安全, 网络数据包捕获, 网络流量分析, 网络通信, 防御绕过, 隐私保护, 零拷贝, 高性能网络库