从用户空间运行 BPF 程序

本文档描述了用于从用户空间运行 BPF 程序的 BPF_PROG_RUN 功能。

概述

可以通过 bpf() 系统调用使用 BPF_PROG_RUN 命令在内核中执行 BPF 程序,并将结果返回到用户空间。这可用于针对用户提供的上下文对象对 BPF 程序进行单元测试,并作为在内核中显式执行程序以产生副作用的方式。该命令之前名为 BPF_PROG_TEST_RUN,并且这两个常量在 UAPI 头文件中继续定义,并别名为相同的值。

BPF_PROG_RUN 命令可用于执行以下类型的 BPF 程序

  • BPF_PROG_TYPE_SOCKET_FILTER

  • BPF_PROG_TYPE_SCHED_CLS

  • BPF_PROG_TYPE_SCHED_ACT

  • BPF_PROG_TYPE_XDP

  • BPF_PROG_TYPE_SK_LOOKUP

  • BPF_PROG_TYPE_CGROUP_SKB

  • BPF_PROG_TYPE_LWT_IN

  • BPF_PROG_TYPE_LWT_OUT

  • BPF_PROG_TYPE_LWT_XMIT

  • BPF_PROG_TYPE_LWT_SEG6LOCAL

  • BPF_PROG_TYPE_FLOW_DISSECTOR

  • BPF_PROG_TYPE_STRUCT_OPS

  • BPF_PROG_TYPE_RAW_TRACEPOINT

  • BPF_PROG_TYPE_SYSCALL

使用 BPF_PROG_RUN 命令时,用户空间提供一个输入上下文对象,以及(对于操作网络数据包的程序类型)包含 BPF 程序将操作的数据包数据的缓冲区。然后,内核将执行该程序并将结果返回到用户空间。请注意,程序在此模式下运行时不会产生任何副作用;特别是,数据包实际上不会被重定向或丢弃,程序返回代码只会返回到用户空间。下面单独记录了用于实时执行 XDP 程序的单独模式。

在“实时帧模式”下运行 XDP 程序

BPF_PROG_RUN 命令具有单独的模式来运行实时 XDP 程序,该模式可用于以一种方式执行 XDP 程序,在这种方式下,数据包将在 XDP 程序执行后由内核实际处理,就像它们到达物理接口一样。当向 BPF_PROG_RUN 提供 XDP 程序时,通过设置 BPF_F_TEST_XDP_LIVE_FRAMES 标志来激活此模式。

实时数据包模式针对多次高性能执行提供的 XDP 程序进行了优化(例如,适合作为流量生成器运行),这意味着语义不如常规测试运行模式那么直接。具体来说:

  • 在实时帧模式下执行 XDP 程序时,执行结果不会返回到用户空间;相反,内核将执行程序返回代码指示的操作(丢弃数据包、重定向数据包等)。因此,在这种模式下运行时,设置系统调用参数中的 data_outctx_out 属性将被拒绝。此外,并非所有失败都会直接报告回用户空间;具体来说,只有在设置或执行期间(如内存分配错误)发生致命错误才会停止执行并返回错误。如果在数据包处理中发生错误(如无法重定向到给定接口),则执行将继续下一次重复;这些错误可以通过与常规 XDP 程序相同的跟踪点来检测。

  • 用户空间可以将 ifindex 作为上下文对象的一部分提供,就像在常规(非实时)模式中一样。XDP 程序将执行,就好像数据包到达此接口一样;也就是说,上下文对象的 ingress_ifindex 将指向该接口。此外,如果 XDP 程序返回 XDP_PASS,则数据包将注入到内核网络堆栈中,就好像它到达该 ifindex 一样;如果它返回 XDP_TX,则数据包将从同一接口传出。但请注意,由于程序执行不是在驱动程序上下文中发生的,因此 XDP_TX 实际上转换为与 XDP_REDIRECT 到同一接口相同的操作(即,只有当驱动程序支持 ndo_xdp_xmit 驱动程序 op 时才有效)。

  • 当多次重复运行程序时,执行将分批进行。批次大小默认为 64 个数据包(与最大 NAPI 接收批次大小相同),但用户空间可以通过 batch_size 参数指定,最多 256 个数据包。对于每个批次,内核重复执行 XDP 程序,每次调用都获得数据包数据的单独副本。对于每次重复,如果程序丢弃数据包,则数据页将立即回收(请参见下文)。否则,数据包将被缓冲,直到批次结束,此时,在此批次期间以此方式缓冲的所有数据包都将一次性传输。

  • 设置测试运行时,内核将初始化一个大小与批次大小相同的内存页池。每个内存页都将使用用户空间在 BPF_PROG_RUN 调用时提供的初始数据包数据进行初始化。在可能的情况下,页面将在未来的程序调用中回收,以提高性能。通常,页面将一次回收整个批次,除非数据包被丢弃(通过返回代码或由于,例如,重定向错误),在这种情况下,该页面将立即回收。如果数据包最终传递到常规网络堆栈(因为 XDP 程序返回 XDP_PASS,或者因为它最终被重定向到将其注入堆栈的接口),则该页面将被释放,并在池为空时分配一个新页面。

    回收时,页面内容不会重写;只有上下文对象中的数据包边界指针(datadata_enddata_meta)将重置为原始值。这意味着如果程序重写了数据包内容,则必须准备好在后续调用中看到原始内容或修改后的版本。