ipcdump 是一个用于跟踪 Linux 上进程间通信(IPC)的工具。它涵盖了大多数常见的 IPC 机制——管道、FIFO、信号、Unix 套接字、基于环回的网络以及伪终端。它是调试多进程应用的有用工具,也是理解系统中不同组件如何相互通信的简单方式。ipcdump 可以跟踪通信的元数据以及内容,特别适合跟踪短生命周期进程之间的 IPC,而这在使用传统调试工具(如 strace 或 gdb)时可能很困难。它还具备一些基本的过滤功能,帮助您筛选大量事件。 ipcdump 收集的大部分信息来自放置在关键内核函数的 kprobes 和 tracepoints 上的 BPF 钩子,虽然它也通过 /proc 文件系统填充一些账目信息。为此,ipcdump 大量使用了 gobpf,它提供了 bcc 框架 的 Go 绑定。
| Ubuntu 18.04 LTS | Ubuntu 20.04 LTS | |
|---|---|---|
| 4.15.0 | 已测试 | 未测试 |
| 5.4.0 | 未测试 | 已测试 |
| 5.8.0 | 未测试 | 已测试* |
*需要从源码构建 bcc
snap install go --classic
或者直接来自 golang 网站
git clone https://github.com/guardicore/IPCDump
cd IPCDump/cmd/ipcdump
go build
./ipcdump -h
Usage of ./ipcdump:
-B uint
每个事件转储的最大字节数,0 表示完整事件(可能很大)。仅在指定 -x 时有效。
-D value
按目标 comm 过滤(可以多次指定)
-L 不输出丢失事件信息
-P value
按 comm 过滤(源或目标均可,可以多次指定)
-S value
按源 comm 过滤(可以多次指定)
-c uint
在 <count> 个事件后退出
-d value
按目标 pid 过滤(可以多次指定)
-f string
<text|json> 输出格式(默认为 text)(默认 "text")
-p value
按 pid 过滤(源或目标均可,可以多次指定)
-s value
按源 pid 过滤(可以多次指定)
-t value
按类型过滤(可以多次指定)。
可选值:a|all k|signal u|unix ud|unix-dgram us|unix-stream t|pty lo|loopback lt|loopback-tcp lu|loopback-udp p|pipe
-x 转储相关 IPC 字节(而非仅事件详情)
以 root 身份运行:
# 转储系统上所有 IPC
./ipcdump
# 转储任意两个进程之间发送的信号
./ipcdump -t kill
# 转储到或来自 pid 1337 的环回 TCP 连接元数据
./ipcdump -t loopback-tcp -p 1337
# 转储来自 Xorg 的 Unix 套接字 IPC 元数据和内容
./ipcdump -t unix -x -S Xorg
# 转储 JSON 格式的管道 I/O 元数据以及内容的前 64 字节
./ipcdump -t pipe -x -B 64 -f json
ipcdump 由一系列收集器组成,每个收集器负责特定类型的 IPC 事件。例如,IPC_EVENT_LOOPBACK_SOCK_UDP 或 IPC_EVENT_SIGNAL。
实际上,所有收集器都是通过连接到 kprobes 和 tracepoints 的 bpf 钩子构建的。然而,它们的实现是完全独立的——没有特别的理由认为我们的信息总是来自 bpf。也就是说,不同的收集器必须共享一个 bpf 模块,因为它们需要共享一些公共代码。为此,我们共享一个 BpfBuilder(本质上是围绕拼接 bcc 代码字符串的包装器),每个收集器将自己的代码注册到该构建器中。然后使用 gobpf 加载完整的 bcc 脚本,每个模块放置它需要的钩子。
目前有两种账目类型在 IPC 收集器之间共享:
SocketIdentifier (internal/collection/sock_id.go) —— 在内核 struct sock* 和使用它们的进程之间进行映射。CommIdentifier (internal/collection/comm_id.go) —— 在 pid 编号和对应的进程名称 (/proc/<pid>/comm) 之间进行映射。
这些账目信息对于短生命周期进程尤其重要;虽然可以在用户模式下通过解析 /proc 稍后补全,但通常当事件到达处理程序时,相关进程可能已经消失。不过,我们有时也会从 /proc 填充信息。这主要发生在 ipcdump 启动之前就已存在的进程上;在这种情况下,我们不会捕获进程命名之类的事件。SocketIdentifier 和 CommIdentifier 某种程度上尝试在 bcc 代码和 /proc 解析之间抽象这种双重性,通过一个统一的 API,尽管它不是非常干净。顺便说一下,在超新版本的 Linux(5.8)中,bpf 迭代器可以完全取代这种账目管理,但为了向后兼容,我们目前可能应该坚持使用钩子和 procfs 范式。事件输出通过通用的 EmitIpcEvent() 函数完成,该函数接受标准事件格式(源进程、目标进程、元数据键值对以及内容),并以统一格式输出。为节省事件带宽,如果未指定 -x 标志,收集器通常不输出 IPC 内容。这是通过 internal/collection/ipc_bytes.go 中的一些巧妙预处理魔法实现的。
欢迎参与!请查看 TODO 了解重要事项。ipcdump 早期的大部分工作可能涉及针对不同内核版本和符号进行调整。