Help us improve your experience.

Let us know what you think.

Do you have time for a two-minute survey?

 
 

eBPF 内核数据平面(技术预览版)

总结 瞻博网络云原生 Contrail 网络 (CN2) 23.3 版支持 Linux 内核的扩展伯克利数据包过滤器 (eBPF) 数据平面。基于 eBPF 的数据平面使程序能够加载到内核中,以实现高性能应用程序。

Linux 内核概述

Linux 内核内存大致分为以下两个方面:

  • 内核空间:操作系统的核心在内核空间中运行。操作系统可以不受限制地访问所有主机资源,包括:内存、存储和 CPU。内核空间严格保留用于运行特权操作系统内核、内核扩展和主机的大多数设备驱动程序。内核空间受到保护,仅运行受信任的代码。

  • 用户空间库:非内核进程(如常规应用程序)在用户空间内运行。在用户空间中运行的代码对硬件资源的访问有限,通常依赖于内核空间来执行特权操作,如磁盘或网络 I/O。用户空间应用程序通过 API 或系统调用从内核请求服务。系统调用在进程和操作系统之间提供接口,以允许用户空间进程请求操作系统的服务。

在某些情况下,开发人员可能需要比通过系统调用接口分配的内核性能更高的内核性能。自定义系统调用、对新硬件的支持和新的文件系统都可能需要额外的内核灵活性。像这样的情况需要在不增加其源代码的情况下对内核进行增强。Linux 内核模块 (LKM) 在不添加或更改其源代码的情况下扩展基本内核。与系统调用不同,LKM 在运行时直接加载到内核中。这意味着不需要在每次需要新的 LKM 时重新编译和重新启动内核。尽管 LKM 扩展了内核,但在涉及高性能数据包处理等用例时,LKM 仍然受到内核本身开销和网络限制的限制。此外,LKM 通常存在需要解决的内核版本兼容性问题,而非上游 LKM 可能存在信任问题。

注意:此功能属于瞻博网络 CN2 技术预览功能。这些功能是“按原样”的,供自愿使用。瞻博网络支持将尝试解决客户在使用这些功能时遇到的任何问题,并代表支持案例创建错误报告。但是,瞻博网络可能无法为技术预览版功能提供全面的支持服务。

有关更多信息,请参阅 瞻博网络 CN2 技术预览版(技术预览版) 或联系 瞻博网络支持

eBPF 概述

伯克利数据包过滤器(BPF)提供了一种有效过滤数据包的方法,避免了从内核到用户空间的无用数据包复制。eBPF 是一种用于编写在内核空间中执行的代码的机制。开发人员使用受限制的 C 语言编写 eBPF 程序。此代码称为字节码,由 Clang/LLVM 等编译器工具链编译和生成。

BPF 的一个安全优势是要求 BPF 程序在内核中处于活动状态之前必须经过验证。BPF 程序在内核中的虚拟机 (VM) 中运行。验证过程可确保 BPF 程序在 VM 中运行完成,而无需循环。验证过程还包括检查有效的寄存器状态、程序的大小和越界跳转。验证过程完成后,程序在内核中变为活动状态。

eBPF 内核数据平面

与传统 LKM 相比,eBPF 内核数据平面使您能够更安全、更高效地扩展和自定义内核的行为。与 LKM 一样,内核不会重新加载,内核的代码也不会更改。内核数据平面中的 eBPF 意味着用户空间应用程序可以在数据流经内核时处理数据。换句话说,利用 eBPF 的应用程序是在内核内编写和处理的,从而大大减少了典型内核进程的网络层开销。高性能数据包处理、数据包过滤、跟踪和安全监控等任务都受益于 eBPF 提供的额外内核性能。

了解 eBPF 计划

事件和挂钩

eBPF 程序在事件驱动的环境中执行。事件是监视、跟踪或分析的特定情况或条件。例如,到达接口的网络数据包就是一个事件。eBPF 程序附加到此事件,可以分析入口网络数据包或跟踪数据包的路径。

挂钩是指将 eBPF 程序附加到内核流中的特定执行点的过程。当钩子触发时,eBPF 程序被执行。eBPF 程序可以修改事件行为或记录与事件相关的数据。例如,系统调用可能充当挂钩。启动系统调用时,会触发钩子,使 eBPF 程序能够监视系统进程。

钩子的其他示例包括:

  • 函数进入和退出:eBPF 程序拦截对预先存在的函数的调用。

  • 网络事件:当指定接口上收到数据包时,程序将执行。

  • KprobesUprobes:程序附加到探测器以获取内核或用户空间功能。

帮助程序函数

eBPF 程序调用帮助程序函数,有效地使 eBPF 程序功能丰富。帮助程序函数执行以下操作:

  • 搜索、更新和删除键值对
  • 生成伪随机数
  • 获取和设置隧道元数据
  • 将 eBPF 程序链接在一起(尾部调用)
  • 使用套接字执行任务,例如绑定、检索 Cookie 和重定向数据包

内核定义了帮助程序函数,这意味着内核将 eBPF 程序进行的调用范围列入白名单。

eBPF 地图

映射是eBPF程序使用的主要数据结构。映射允许内核和用户空间之间的数据通信。映射是一种键值存储,其中的值被视为任意数据的二进制 blob。当不再需要该映射时,将通过关闭关联的文件描述符将其删除。

每个映射具有以下四个属性:其类型、可以包含的最大元素数、其值的大小(以字节为单位)及其键的大小(以字节为单位)。有多种地图类型可供选择,每种类型都提供不同的行为和权衡。通过使用 bpf_map_lookup_elem() 和 bpf_map_update_elem() 函数,可以从 eBPF 程序和用户空间程序访问和操作所有映射。

执行 eBPF 程序

内核期望所有 eBPF 程序都作为字节码加载。因此,eBPF程序必须使用工具链编译成字节码。编译完成后,eBPF 程序在部署在指定的钩子点之前,会在内核内进行验证。eBPF 支持现代架构,这意味着它已升级为 64 位编码,总共有 11 个寄存器。这将 eBPF 与 x86_64、ARM 和 arm64 架构等的硬件紧密对应。

最重要的是,eBPF 解锁了对内核级事件的访问,避免了与直接更改内核代码相关的典型约束。总之,eBPF的工作原理是:

  • 将 eBPF 程序编译为字节码

  • 验证程序在挂接点加载的 VM 中安全执行

  • 将程序附加到内核中由指定事件触发的挂钩点

  • 在运行时编译为本机字节码以提高可移植性

  • 调用帮助程序函数以在程序运行时操作数据

  • 使用映射(键值对)在用户空间和内核空间之间共享数据并维护状态

    图 1:eBPF 程序执行 eBPF Program Execution

XDP 概述

eXpress 数据路径 (XDP) 是内核网络堆栈中较新的可编程层。XDP 是附加和执行 eBPF 字节码的较新钩点之一。XDP 对象文件加载在多个内核和架构上,无需传统 eBPF 程序所需的重新编译。尽管与内核旁路解决方案相比,XDP 程序缩小了性能差距,但这些程序不会绕过内核。以下是定义 XDP 的一些关键特征:

  • 不打算比内核旁路更快

  • 直接在数据包缓冲区上运行

  • 减少每个数据包执行的指令数

内核网络堆栈专为套接字交付用例而设计。因此,它始终将所有传入数据包转换为套接字缓冲区 (SKB)。XDP 允许在数据包转换为 SKB 之前对其进行处理。XDP 与现有内核堆栈协同运行,所有这些都不需要修改内核。这使得 XDP 成为提供更大灵活性的内核内替代方案。

XDP 和流量控制挂钩

所有 eBPF 字节码加载、映射创建、更新和读取都通过 BPF 系统调用进行处理。请务必注意,XDP 驱动程序挂钩仅在入口处可用。如果必须处理出口数据包,则可能需要使用替代钩子,例如 TC(流量控制)。在更广泛的层面上,XDP BPF 程序和 TC BPF 程序之间存在一些差异:

  • XDP 挂钩发生得更早,从而提高了性能。

    • TC 挂钩稍后出现。因此,他们可以访问套接字缓冲区 (sk_buff) 结构和字段。这是造成 XDP 和 TC 挂钩之间性能差异的重要因素,因为 sk_buff 数据结构包含元数据和信息,使内核的网络堆栈能够有效地处理和路由数据包。访问 sk_buff 结构及其属性会产生一定的开销,因为内核堆栈必须分配、提取元数据和管理数据包,直到它到达 TC 钩子。

  • TC 挂钩可实现更好的数据包整改。

  • XDP 更适合完全重写数据包。

    • sk_buff 用例包含大量特定于协议的详细信息,例如与 GSO(通用分段卸载)相关的状态信息。这使得仅通过重写数据包数据来切换协议变得具有挑战性。因此,需要通过 BPF 帮助程序函数完成进一步的转换。这些帮助程序函数可确保正确转换 的内部 sk_buff 组件。

      相比之下, xdp_buff 用例不会遇到这些问题。它在数据包处理的早期阶段运行,甚至在内核分配 sk_buff.因此,在这种情况下进行任何类型的数据包重写都非常简单。

  • TC eBPF 和 XDP 作为补充计划。

    • 在用例需要数据包重写和复杂数据处理的情况下,可以通过运行这两种类型的互补程序来克服每种程序类型的限制。例如,入口点的 XDP 程序可以执行完整的数据包重写,并将自定义元数据从 XDP BPF 传输到 TC BPF。然后,TC BPF 程序可以利用 XDP 元数据和 sk_buff 字段来执行复杂的数据包处理。

  • TC BPF 不需要更改硬件驱动程序。XDP 使用本机驱动程序模式以获得最佳性能。

    图 2:XDP 和 TC 挂钩 XDP and TC Hooks

XDP 控制和数据平面

使用 XDP 时,数据平面驻留在内核内,将其分为两个组件:内核核心和内核内 eBPF 程序。控制平面驻留在用户空间中,与内核空间一起操作以执行基本任务,例如将 BPF 程序加载到内核中以及通过使用映射管理程序行为。

XDP 操作和行为

在 XDP 挂钩点运行的 eBPF 程序根据程序处理数据包的方式返回以下操作:

  • 将入口数据包重定向到其他接口或用户空间。
  • 将收到的数据包从它到达的同一接口发送回去。

  • 将数据包发送到内核堆栈进行处理。

  • 丢弃数据包。

XDP 和 eBPF

XDP 充当内核网络堆栈的软件卸载层。一个理想的用例是与 Linux 内核的网络堆栈结合使用以提高性能。例如,在 IP 路由用例中,内核处理路由表管理和邻居查找,而 XDP 通过使用帮助程序函数高效访问这些路由表来加速数据包处理。查找完成并识别下一跃点后,将相应地修改数据包标头。如果不需要修改,数据包将按原样转发到内核。

请务必注意,XDP 对 RX 内存模型施加了以下限制:

  • 目前不支持所有流行驱动程序的巨型帧,尽管 i40e、Veth 和 virtio 支持巨型帧。
  • 内核空间中存在组播支持

与专有内核模块(如 vRouter 内核模块)相比,利用基于 eBPF XDP 驱动程序模式的方法具有优势。其中一些好处包括改进的生命周期管理 (LCM)、安全性和性能。与集成到 Linux 上游树中的内核模块相比,维护备用 eBPF XDP 内核程序在不同内核版本中更有效。这是因为内核依赖项仅限于一小部分 eBPF 帮助程序函数。

请务必注意,在控制器/库崩溃的情况下,XDP 或 TC 程序仍会转发流量。重新连接或更换 XDP 或 TC 程序不会中断网络流量。

内核到用户空间通信

AF_XDP套接字用于促进在 XDP 挂钩上运行的内核空间程序与用户空间库之间的通信。AF_XDP 是在 Linux 4.1.8 中引入的。AF_XDP 是一种新的套接字类型,用于将原始数据包快速传送到用户空间应用程序。AF_XDP利用 Linux 内核驱动程序实现高性能和可扩展性。值得注意的是,它还提供以下功能:支持零拷贝数据传输、对中断的最小依赖,以及对网络驱动程序的优化,类似于数据平面开发工具包 (DPDK) 和矢量数据包处理 (VPP) 提供的功能。

流经AF_XDP套接字的数据包路径如下:

特定于 NIC 的驱动程序访问 NIC 上的传入数据包。驱动程序通过运行在 XDP 挂钩处的 eBPF 程序拦截并处理它们,该程序绑定到相应的接口。XDP 程序单独处理每个数据包,完成后,它会生成一个操作,如 XDP 操作和合作部分所述。通过利用XDP_REDIRECT,数据包通过与该接口关联的AF_XDP套接字定向到用户空间。这样就可以直接从 NIC 接收原始数据包。通过绕过内核空间,可以避免内核开销,数据包可以直接访问用户空间库。

支持的 eBPF 功能

CN2 23.2 版中支持的功能

CN2 版本 23.3 中支持的功能

启用 eBPF 数据平面

您可以通过在虚拟路由器的规格中指定 agentModeType: xdp 来启用 eBPF 数据平面功能。由于并非所有 XDP 驱动程序当前都支持巨型帧,因此 CN2 部署程序会设置结构、环路和 veth 接口的最大传输单元 (MTU) 值。

以下示例是启用了 XDP-eBPF 的 vRouter 资源。

记下该 name: contrail-vrouter-masters 字段。由于此 vRouter 被指定为主节点,因此所有可用的主节点都将设置为 agentModeType: xdp 应用部署程序时。此逻辑也适用于工作器节点;如果在部署程序中为工作节点资源指定“xdp”,则会为所有工作器节点配置 XDP-eBPF。

XDP 支持的驱动程序

下表列出了具有 eBPF-XDP 数据路径的 CN2 验证的 XDP 驱动程序。

表 1:经过验证的 XDP 驱动程序
驱动程序名称 基础平台
埃纳 Aws
维斯 容器连接
维蒂奥 CN2-On-CN2
i40e 裸机
注意:

如果您的集群对 NIC 接口使用 AWS ENA 底层驱动程序,部署程序会为物理接口设置 4 个接收 (RX) 和 4 个传输 (TX) 的队列配置。发生此更改的原因是 XDP 不支持 ENA 驱动程序的默认 RX 和 TX 队列计数。

eBPF 数据平面设计和 CN2 实现

eBPF 内核数据平面由两个核心组件组成:内核内 XDP 程序和补充的用户空间库。这个 eBPF 驱动的数据平面是 vRouter 的第三个数据平面选项,还有基于内核模块的 vRouter 和 DPDK。以下各节将介绍此技术提供的各种数据平面功能的设计和实现细节。

eBPF 数据平面组件

如前所述,eBPF 数据平面基于 XDP。对于需要数据包源自默认命名空间的功能,可能还需要出口 TC 挂钩。这是由于出口路径上没有 XDP 挂钩。TC 程序还引入了新的解析函数,因为 TC 钩子与 SKB 一起运行。

图 3:eBPF 数据平面组件 eBPF Data Plane Components

eBPF 数据平面不用作单独的守护程序;相反,它将作为库集成到控制器中(对于 CN2,则为 vRouter 代理)。此实现分为两个组件:

  • 用户空间库:用户空间库充当内核 eBPF 程序和在用户空间中运行的控制器(vRouter 代理)之间的接口。它公开 vRouter 代理用来对数据路径进行编程的 API。此组件执行以下功能:

    • 将 eBPF 程序加载和卸载到内核中

    • 将 eBPF 程序绑定到接口

    • 配置和读取映射条目

    • 转发、修改和过滤用户空间数据路径中的数据包

  • 内核空间程序:这些 eBPF 程序构成了在内核核心中运行的主要数据路径。此程序加载到接口的 XDP 挂钩上,并处理这些接口的所有传入数据包。TC 也用于环路接口的出口。如前所述,用户空间 eBPF 库负责将 eBPF 程序加载到内核核心中。内核空间程序执行以下操作:

    • 转发、修改和过滤内核中的数据包

    • 使用帮助程序函数与内核的其余部分进行交互

    • 创建、更新和读取映射(映射是用户空间库和内核空间程序之间共享的数据结构)

vRouter 代理通过用户空间库以多种方式与 eBPF 数据平面进行交互:

  • 控制器(vRouter 代理)直接调用用户空间 eBPF 库公开的 API。

    • 库公开的公共 API 取代了用于其他数据路径的 ksync/Sandesh 层提供的功能

    • 代理向用户空间库注册回调,用于流未命中和接收感兴趣的数据包

  • 该库还侦听 netlink 套接字事件;用户空间库可以侦听来自内核的路由、ARP 和地址更新
  • 该库还会向内核查询其他信息