BPF_PROG_TYPE_CGROUP_SYSCTL

本文档描述了 BPF_PROG_TYPE_CGROUP_SYSCTL 程序类型,该类型为 sysctl 提供 cgroup-bpf 钩子。

该钩子必须附加到 cgroup,并且每次该 cgroup 内的进程尝试从 proc 中的 sysctl 旋钮读取或写入时都会被调用。

1. 附加类型

BPF_CGROUP_SYSCTL 附加类型必须用于将 BPF_PROG_TYPE_CGROUP_SYSCTL 程序附加到 cgroup。

2. 上下文

BPF_PROG_TYPE_CGROUP_SYSCTL 提供从 BPF 程序访问以下上下文:

struct bpf_sysctl {
    __u32 write;
    __u32 file_pos;
};
  • write 表示是否正在读取 (0) 或写入 (1) sysctl 值。此字段为只读。

  • file_pos 表示正在访问(读取或写入)的 sysctl 的文件位置。此字段是可读写的。写入该字段会设置 sysctl proc 文件中的起始位置,read(2) 将从该位置读取,或者 write(2) 将写入该位置。例如,通过在 write(2) 上使用 bpf_sysctl_set_new_value(),即使当用户空间在 file_pos > 0 上调用时,也可以通过写入零来覆盖整个 sysctl 值。写入非零值可用于访问从指定 file_pos 开始的 sysctl 值的一部分。并非所有 sysctl 都支持使用 file_pos != 0 进行访问,例如,对数值 sysctl 条目的写入必须始终在文件位置 0。另请参阅 kernel.sysctl_writes_strict sysctl。

有关如何访问上下文字段的更多详细信息,请参阅 linux/bpf.h

3. 返回代码

BPF_PROG_TYPE_CGROUP_SYSCTL 程序必须返回以下返回代码之一:

  • 0 表示“拒绝访问 sysctl”;

  • 1 表示“继续访问”。

如果程序返回 0,用户空间将从 read(2)write(2) 中获取 -1,并且 errno 将设置为 EPERM

4. 辅助函数

由于 sysctl 旋钮由名称和值表示,因此 sysctl 特定的 BPF 辅助函数专注于提供对这些属性的访问。

  • bpf_sysctl_get_name() 将 sysctl 名称(如在 /proc/sys 中可见的名称)获取到 BPF 程序提供的缓冲区中;

  • bpf_sysctl_get_current_value() 将 sysctl 当前持有的字符串值获取到 BPF 程序提供的缓冲区中。此辅助函数在从 sysctl read(2)write(2) 时都可用;

  • bpf_sysctl_get_new_value() 在实际写入发生之前,获取当前正在写入 sysctl 的新字符串值。此辅助函数只能在 ctx->write == 1 时使用;

  • bpf_sysctl_set_new_value() 在实际写入发生之前,覆盖当前正在写入 sysctl 的新字符串值。sysctl 值将从当前的 ctx->file_pos 开始被覆盖。如果必须覆盖整个值,则 BPF 程序可以在调用辅助函数之前将 file_pos 设置为零。此辅助函数只能在 ctx->write == 1 时使用。辅助函数设置的新字符串值由内核以与用户空间传递的等效字符串相同的方式进行处理和验证。

BPF 程序看到的 sysctl 值与用户空间在 proc 文件系统中看到的相同,即作为字符串。由于许多 sysctl 值表示整数或整数向量,因此可以使用以下辅助函数从字符串中获取数值:

  • bpf_strtol() 将字符串的初始部分转换为长整型,类似于用户空间的 strtol(3)

  • bpf_strtoul() 将字符串的初始部分转换为无符号长整型,类似于用户空间的 strtoul(3)

有关此处描述的辅助函数的更多详细信息,请参阅 linux/bpf.h

5. 示例

请参阅 test_sysctl_prog.c,其中包含 C 语言的 BPF 程序示例,该程序访问 sysctl 名称和值,解析字符串值以获取整数向量,并使用结果来决定是否允许或拒绝访问 sysctl。

6. 注意事项

BPF_PROG_TYPE_CGROUP_SYSCTL 旨在在受信任的 root 环境中使用,例如,监控 sysctl 使用情况或捕获以单独 cgroup 中的 root 身份运行的应用程序尝试设置的不合理的值。

由于在 sys_read / sys_write 时调用 task_dfl_cgroup(current),因此它可能会返回与 sys_open 时不同的结果,即在 proc 文件系统中打开 sysctl 文件的进程可能与尝试读取/写入该文件的进程不同,并且这两个进程可能在不同的 cgroup 中运行,这意味着不应将 BPF_PROG_TYPE_CGROUP_SYSCTL 用作限制 sysctl 使用的安全机制。

与任何 cgroup-bpf 程序一样,如果运行在 cgroup 中的 root 身份应用程序不应被允许分离/替换管理员附加的 BPF 程序,则应格外小心。