首页 > 解决方案 > ebpf:验证者在哪里打印它的消息?

问题描述

验证者在哪里打印它的消息?我嵌入了一个简单的代码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()返回EACCESS错误并且无法加载,这是预期的,但我想要验证器消息(dmesg 或控制台中没有)。

标签: socketsbpfebpf

解决方案


当试图加载一个 eBPF 程序时,加载器需要将一个缓冲区传递给内核验证器,然后打印它以获得验证器的输出。

验证者将使用用户空间程序提供的这个缓冲区,并在其中打印所有日志。除了极少数特定消息外,它不会将任何内容打印到内核日志或控制台(由您的 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;
    }

这是我们尝试加载程序的部分。我们bpf_load_program()从 libbpf 调用,并按此顺序传递程序类型、指令、指令数量、许可证字符串、与内核版本相关的一些标志,最后是:一个空缓冲区及其大小。大小BPF_LOG_BUF_SIZE是非空的(在tools/lib/bpf/bpfas中定义(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;
    }

注意:除了缓冲区和缓冲区的大小之外,加载器必须将一个log_level整数传递给验证器,以告诉它应该使用什么级别的详细信息。如果值为 at 0,则验证程序不向缓冲区打印任何内容。在当前情况下,我们不log_level直接处理。bpf_load_program()也不会将值设置为0最终会调用libbpf__bpf_prog_load()libbpf。该函数尝试在不更改 的情况下首次加载程序log_level,但如果失败,它会使用log_levelset重新尝试1- 有关详细信息,请参阅注释中的 Mark 指针。不同的值log_level内部内核头文件中定义并且不是用户 API 的一部分,这意味着验证程序关于日志详细程度的行为可能因内核版本而异。


推荐阅读