ebpf:验证者在哪里打印它的消息?

ebpf: where verifier prints its messages?

验证器在哪里打印消息?我在 struct bpf_insn 中嵌入了一个简单的代码,我将其加载并附加为 BPF_PROG_TYPE_SOCKET_FILTER 类型:

struct bpf_insn prog[] = {
   BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
   BPF_EXIT_INSN(),
};

这段代码是故意弄错的(R0退出前没有初始化)。 bpf_prog_load() returns EACCESS 错误并且无法加载,这是预期的,但我想要验证程序消息(dmesg 或控制台中没有任何内容)。

尝试加载 eBPF 程序时,由加载程序将缓冲区传递给内核验证程序并稍后打印它 得到验证者的输出。

验证器将使用用户 space 程序提供的缓冲区,并在其中打印所有日志。除了极少数特定消息外,它 不会 将任何内容打印到内核日志或控制台(由您的 shell 处理,而不是直接由内核处理)。

让我们看一下您在评论中提到的 samples/bpf/sock_example.c 中的片段。

    prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt,
                   "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
    if (prog_fd < 0) {
        printf("failed to load prog '%s'\n", strerror(errno));
        goto cleanup;
    }

这是我们尝试加载程序的部分。我们从 libbpf 中调用 bpf_load_program(),然后按顺序传递程序类型、指令、指令数、许可证字符串、一些与内核版本相关的标志,最后:一个空缓冲区和它的大小。大小 BPF_LOG_BUF_SIZE 是非空的(在 tools/lib/bpf/bpf 中定义为 (UINT32_MAX >> 8))。

函数 bpf_load_program() 会将所有这些信息(包括指向缓冲区的指针)传递给 bpf() 系统调用,后者将尝试加载程序。验证者将用日志填充缓冲区(无论加载是否成功,但请参阅底部的注释)。然后再次由加载程序使用这些日志。函数 bpf_load_program() 是低级的,它对缓冲区中验证者的日志 什么都不做 ,即使加载失败也是如此。它留给调用者处理或转储日志。您尝试 运行 的示例应用程序也什么都不做;因此,缓冲区未被使用,您不会在控制台中看到日志。

要查看日志,在您的情况下,您可能只需要转储此缓冲区。像下面这样简单的东西应该可以工作:

    ...
    if (prog_fd < 0) {
        printf("failed to load prog '%s'\n", strerror(errno));
        printf("%s", bpf_log_buf);
        goto cleanup;
    }

注意:除了buffer和buffer的大小,loader还必须传递一个log_level整数给验证者,告诉它应该使用什么级别的冗长。如果值为 0,验证程序不会向缓冲区打印任何内容。在当前情况下,我们不直接处理 log_levelbpf_load_program() 也没有并将值设置为 0 但是 它最终在 libbpf 中调用 libbpf__bpf_prog_load()。该函数尝试在不更改 log_level 的情况下第一次加载程序,但如果失败,它会重新尝试将 log_level 设置为 1 - 请参阅 Mark 在评论详情。 log_level 的不同值在 internal kernel headers 中定义,不属于用户 API,这意味着验证者关于日志详细程度的行为可能因内核版本而异。