19.4. EBPF和XDP

19.4.1. 介绍

EBPF代表扩展的BPF。这是伯克利包过滤器的扩展版本,在最新的Linux内核版本中可用。

它为在C语言中开发的ebpf程序提供了更高级的特性,并且能够使用内核和用户空间之间共享的结构化数据。

ebpf在Suricata中有三种用途:

  • EBPF滤波器:可以开发任何类似于BPF的滤波器。提供了一个只接受某些VLAN数据包的过滤器示例。还提供了旁路实现。

  • eBPF负载平衡:提供可编程的负载平衡。提供简单的ippair负载平衡。

  • XDP程序:Suricata可以加载XDP程序。提供旁路程序。

旁路可以在ebpf和xdp中实现。xdp的优点是,包在可能的最早阶段被丢弃。所以性能更好。但是绕过的数据包不能到达网络,因此不能在常规流量上使用它,只能在重复/嗅探流量上使用。

eBPF依赖于最强大的旁路的概念之一。映射是在用户空间和内核空间/硬件之间共享的数据结构。它允许用户空间和内核空间进行交互,传递信息。映射通常被实现为可以包含任意键、值对的数组或哈希表。

19.4.1.1. XDP

XDP提供了另一种Linux本机方法来优化Suricata在高速网络上的性能:

XDP或eXpress数据路径作为IO-Visor项目的一部分,在Linux内核中提供了一个高性能、可编程的网络数据路径。XDP在软件栈的最低点提供裸金属包处理,这使得它在不影响可编程性的情况下实现速度的理想选择。此外,新函数可以在不修改内核的情况下,通过集成的快速路径动态实现。

有关XDP的详细信息:

19.4.2. 要求

您将需要一个支持XDP的内核,为了最大程度地提高性能,还需要一个在驱动程序中支持XDP的网卡。

Suricata xdp代码已经用4.13.10进行了测试,但是4.15或更高版本对于使用CPU重定向映射等所有功能是必需的。

如果您使用的是Intel网卡,则需要使用树内内核NIC驱动程序。树外驱动程序不包含xdp支持。

拥有一个支持RSS对称散列的网卡是一个很好的方法,否则您将不得不使用xdp cpu重定向映射功能。

19.4.3. 先决条件

此指南已在Debian/Ubuntu“LTS”Linux上得到确认。

19.4.3.1. 禁用irqbalance

irqbalance 在这里描述的大多数设置中可能会导致问题,因此建议停用它:

systemctl stop irqbalance
systemctl disable irqbalance

19.4.3.2. 内核

您需要运行内核4.13或更高版本。

19.4.3.3. 叮当声和从属关系

确保你有 clang (>=3.9)安装在系统上:

sudo apt install clang

还需要一些i386头文件,因为eBPF不是x86_64,而且一些包含的头文件是特定于体系结构的:

sudo apt install libc6-dev-i386 --no-install-recommends

19.4.3.4. 伦敦银行同业拆借利率

Suricata使用libbpf与ebpf和xdp交互:

git clone https://github.com/libbpf/libbpf.git

现在,您可以构建和安装库:

cd libbpf/src/
make && sudo make install

sudo make install_headers
sudo ldconfig

在某些情况下,系统将找不到安装在下的libbpf库 /usr/lib64 因此,您可能需要修改ldconfig配置。

19.4.4. 编译并安装Suricata

要获取Suricata源,您可以使用通常的:

git clone https://github.com/OISF/suricata.git
cd suricata && git clone https://github.com/OISF/libhtp.git -b 0.5.x

./autogen.sh

然后您需要添加eBPF标志来配置和指定构建所有C源代码(包括eBPF程序)的Clang编译器:

CC=clang ./configure --prefix=/usr/ --sysconfdir=/etc/ --localstatedir=/var/ \
--enable-ebpf --enable-ebpf-build

make clean && make
sudo make install-full
sudo ldconfig
sudo mkdir /usr/libexec/suricata/ebpf/

这个 clang 如果您想构建eBPF文件,则需要编译器,因为构建是通过llvm/clang suite中提供的特定eBPF后端完成的。如果您不想使用Clang来构建Suricata本身,您仍然可以使用 --with-clang 参数::

./configure --prefix=/usr/ --sysconfdir=/etc/ --localstatedir=/var/ \
--enable-ebpf --enable-ebpf-build --with-clang=/usr/bin/clang

19.4.5. 设置旁路

如果计划使用ebpf或xdp绕过内核/硬件级别,则需要启用以下某些功能:

首先,启用 bypassstream 段在 suricata.yaml ::

stream:
  bypass: true

一旦达到水流深度,这将绕过水流。

如果需要,还可以通过设置 encryption-handlingbypass 在应用层TLS部分:

app-layer:
  protocols:
    tls:
      enabled: yes
      detection-ports:
        dp: 443

      encryption-handling: bypass

另一种解决方案是使用一组签名 bypass 关键字获得选择性旁路。Suricata Traffic ID定义可用于其他签名的流位。例如,可以使用:

alert any any -> any any (msg:"bypass video"; flowbits:isset,traffic/label/video; noalert; bypass; sid:1000000; rev:1;)
alert any any -> any any (msg:"bypass Skype"; flowbits:isset,traffic/id/skype; noalert; bypass; sid:1000001; rev:1;)

19.4.6. 设置EBPF筛选器

文件 ebpf/vlan_filter.c 包含交换机中VLAN id的列表,您需要编辑该列表以使某些内容适应您的网络。中还提供了另一个从IPv4地址集丢弃数据包的筛选器 ebpf/filter.c . 见 固定映射用法 更多信息。

Suricata可以作为ebpf过滤器加载任何暴露 filter 部分。

一旦修改并通过 make 完成后,您可以根据需要复制生成的eBPF筛选器::

cp ebpf/vlan_filter.bpf /usr/libexec/suricata/ebpf/

然后设置 ebpf-filter-file 中的af packet部分的变量 suricata.yaml ::

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_flow # choose any type suitable
  defrag: yes
  # eBPF file containing a 'filter' function that will be inserted into the
  # kernel and used as load balancing function
  ebpf-filter-file:  /usr/libexec/suricata/ebpf/vlan_filter.bpf
  use-mmap: yes
  ring-size: 200000

然后你可以正常运行Suricata::

/usr/bin/suricata --pidfile /var/run/suricata.pid  --af-packet=eth3 -vvv

19.4.7. 设置EBPF旁路

您也可以使用EBPF旁路。为此,加载 bypass_filter.bpf 文件和更新中的AF数据包配置 suricata.yaml 将旁路设置为 yes ::

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_qm # symmetric RSS hashing is mandatory to use this mode
  # eBPF file containing a 'filter' function that will be inserted into the
  # kernel and used as packet filter function
  ebpf-filter-file:  /usr/libexec/suricata/ebpf/bypass_filter.bpf
  bypass: yes
  use-mmap: yes
  ring-size: 200000

对eBPF代码具有旁路兼容代码的约束比常规过滤器的约束更强。过滤器必须暴露 flow_table_v4flow_table_v6 每个CPU阵列映射的定义与 bypass_filter.c . Suricata将访问和维护这两个映射,以处理要绕过的流列表。

如果不使用VLAN跟踪 (vlan.use-for-tracking 设置为 false 在苏瑞塔亚姆)然后你还必须设置 VLAN_TRACKING 定义为 0 在里面 bypass_filter.c .

19.4.8. 设置EBPF负载平衡

ebpf负载平衡允许使用ebpf过滤器中实现的任何逻辑来平衡侦听套接字上的流量。用标记的函数返回的值 loadbalancer 段与CPU计数上的模块一起使用,以知道数据包必须发送到哪个套接字。

中提供了一个简单的对称IP对哈希函数的实现 lb.bpf 文件。

根据需要复制生成的EBPF筛选器::

cp ebpf/lb.bpf /usr/libexec/suricata/ebpf/

然后使用 cluster_ebpf 作为af包接口部分的负载平衡方法,指出 ebpf-lb-file 变量到 lb.bpf 文件::

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_ebpf
  defrag: yes
  # eBPF file containing a 'loadbalancer' function that will be inserted into the
  # kernel and used as load balancing function
  ebpf-lb-file:  /usr/libexec/suricata/ebpf/lb.bpf
  use-mmap: yes
  ring-size: 200000

19.4.9. 设置xdp旁路

XDP旁路允许Suricata告诉内核某些流的数据包必须通过XDP机制丢弃。这是在数据报到达Linux内核网络堆栈之前发生的早期删除。

建议使用Linux 4.15或更高版本。如果设置了 BUILD_CPUMAP0 在里面 ebpf/xdp_filter.c .

根据需要复制生成的XDP筛选器::

cp ebpf/xdp_filter.bpf /usr/libexec/suricata/ebpf/

在中设置AF数据包部分/接口 suricata.yaml .

我们将使用 cluster_qm 因为我们在NIC上有对称散列, xdp-mode: driver 我们还将使用 /usr/libexec/suricata/ebpf/xdp_filter.bpf (在我们的示例中,TCP卸载/绕过)::

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_qm # symmetric hashing is a must!
  defrag: yes
  # Xdp mode, "soft" for skb based version, "driver" for network card based
  # and "hw" for card supporting eBPF.
  xdp-mode: driver
  xdp-filter-file:  /usr/libexec/suricata/ebpf/xdp_filter.bpf
  # if the ebpf filter implements a bypass function, you can set 'bypass' to
  # yes and benefit from these feature
  bypass: yes
  use-mmap: yes
  ring-size: 200000
  # Uncomment the following if you are using hardware XDP with
  # a card like Netronome (default value is yes)
  # use-percpu-hash: no

xdp旁路与af_packet ips模式兼容。绕过流的数据包将直接从一个卡发送到第二个卡,而不经过内核网络堆栈。

如果您使用硬件xdp卸载,则可能需要设置 use-percpu-hash 设置后生成并安装xdp过滤器文件 USE_PERCPU_HASH 到0。

在xdp过滤器文件中,可以设置 ENCRYPTED_TLS_BYPASS 如果要绕过EBPF代码中加密的TLS 1.2数据包,则返回1。请注意,这意味着Surica将对端口443上具有正确模式的数据包视而不见。

如果不使用VLAN跟踪 (vlan.use-for-tracking 在suricata.yaml中设置为false),然后还必须将vlan_跟踪定义设置为0 in xdp_filter.c .

19.4.9.1. Intel NIC安装程序

Intel网卡不支持对称散列,但可以使用特定的散列函数来模拟它。

严格遵守这些说明以获得预期结果:

ifconfig eth3 down

在树内核驱动程序中使用:xdp支持在Intel网站上提供的Intel驱动程序中不可用。

启用对称哈希::

ifconfig eth3 down
ethtool -L eth3 combined 16 # if you have at least 16 cores
ethtool -K eth3 rxhash on
ethtool -K eth3 ntuple on
ifconfig eth3 up
./set_irq_affinity 0-15 eth3
ethtool -X eth3 hkey 6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A equal 16
ethtool -x eth3
ethtool -n eth3

在上述设置中,您可以自由使用任何最近 set_irq_affinity 脚本。它可用于任何Intel X520/710 NIC源驱动程序下载。

注: 我们使用一个特殊的低熵密钥进行对称散列。 More info about the research for symmetric hashing set up

19.4.9.2. 禁用任何NIC卸载

运行以下命令以禁用卸载:

for i in rx tx tso ufo gso gro lro tx nocache copy sg txvlan rxvlan; do
       /sbin/ethtool -K eth3 $i off 2>&1 > /dev/null;
done

19.4.9.3. 尽量保持平衡

尽量使用网卡的流量:

for proto in tcp4 udp4 ah4 esp4 sctp4 tcp6 udp6 ah6 esp6 sctp6; do
   /sbin/ethtool -N eth3 rx-flow-hash $proto sd
done

此命令仅使用源IP和目标IP触发负载平衡。就负载平衡公平性而言,这可能不是最佳选择,但这可确保流中的所有数据包即使在IP碎片的情况下(源端口和目标端口对某些碎片化数据包不可用)也会到达同一线程。

19.4.9.4. xdp cpu重定向案例

如果您的硬件无法进行对称负载平衡,但在驱动程序模式下支持XDP,则可以使用中提供的CPU重定向映射支持 xdp_filter.bpfxdp_lb.bpf 文件。在这种模式下,负载平衡将由XDP过滤器完成,每个CPU将处理整个包处理,包括在内核中创建skb结构。

您需要Linux 4.15或更高版本才能使用该功能。

为此,设置 xdp-cpu-redirect 一组CPU的AF数据包接口配置中的变量。然后使用 cluster_cpu 作为负载平衡功能。您还需要设置关联性,以确保Surifica使用了具有skb分配的CPU内核。

另外,为了避免数据包无序,您需要将RSS队列编号设置为1。所以如果我们的接口是 eth3 ::

/sbin/ethtool -L eth3 combined 1

如果您的系统有超过64核,您需要设置 CPUMAP_MAX_CPUS 一个比这个数字大的值 xdp_lb.cxdp_filter.c .

纯XDP负载平衡的示例配置如下所示:

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_cpu
  xdp-mode: driver
  xdp-filter-file:  /usr/libexec/suricata/ebpf/xdp_lb.bpf
  xdp-cpu-redirect: ["1-17"] # or ["all"] to load balance on all CPUs
  use-mmap: yes
  ring-size: 200000

可以使用 xdp_monitor 获取有关CPU重定向行为的信息。此程序在Linux树中的 samples/bpf 目录,将由make命令生成。样本输出如下:

sudo ./xdp_monitor --stats
XDP-event       CPU:to  pps          drop-pps     extra-info
XDP_REDIRECT    11      2,880,212    0            Success
XDP_REDIRECT    total   2,880,212    0            Success
XDP_REDIRECT    total   0            0            Error
cpumap-enqueue   11:0   575,954      0            5.27       bulk-average
cpumap-enqueue  sum:0   575,954      0            5.27       bulk-average
cpumap-kthread  0       575,990      0            56,409     sched
cpumap-kthread  1       576,090      0            54,897     sched

19.4.9.5. 用xdp开始测量

您现在可以在激活xdp旁路的情况下开始Suricata::

/usr/bin/suricata -c /etc/suricata/xdp-suricata.yaml --pidfile /var/run/suricata.pid  --af-packet=eth3 -vvv

确认xdp过滤器已进入输出(示例)::

...
...
(runmode-af-packet.c:220) <Config> (ParseAFPConfig) -- Enabling locked memory for mmap on iface eth3
(runmode-af-packet.c:231) <Config> (ParseAFPConfig) -- Enabling tpacket v3 capture on iface eth3
(runmode-af-packet.c:326) <Config> (ParseAFPConfig) -- Using queue based cluster mode for AF_PACKET (iface eth3)
(runmode-af-packet.c:424) <Info> (ParseAFPConfig) -- af-packet will use '/usr/libexec/suricata/ebpf/xdp_filter.bpf' as XDP filter file
(runmode-af-packet.c:429) <Config> (ParseAFPConfig) -- Using bypass kernel functionality for AF_PACKET (iface eth3)
(runmode-af-packet.c:609) <Config> (ParseAFPConfig) -- eth3: enabling zero copy mode by using data release call
(util-runmodes.c:296) <Info> (RunModeSetLiveCaptureWorkersForDevice) -- Going to use 8 thread(s)
...
...

19.4.10. 固定映射用法

如果创建过程消失,并且外部工具也可以访问固定的贴图,则锁定的贴图将保持附着在系统上。在Suricata旁路的情况下,这可以用来保持被旁路的流量表处于活动状态,所以在重新启动时,Suricata不会被先前被旁路的流量击中。在socket过滤器的情况下,这可以用来维护Suricata之外的工具的映射。

要使用固定地图,首先必须安装 bpf 伪文件系统:

sudo mount -t bpf none /sys/fs/bpf

您还可以添加到 /etc/fstab ::

bpffs                      /sys/fs/bpf             bpf     defaults 0 0

并运行 sudo mount -a .

固定地图将作为文件从 /sys/fs/bpf 目录。苏里卡塔会用这个名字把它们钉住。 suricata-$IFACE_NAME-$MAP_NAME .

要激活接口的固定映射,请设置 pinned-mapstrueaf-packet 此接口的配置:

- interface: eth3
  pinned-maps: true

19.4.11. XDP和固定映射

此选项可用于将套接字筛选器的映射公开给其他进程。例如,这允许对IP地址的接受列表或阻止列表进行外部处理。参见 bpfctrl 例如外部列表处理。

对于xdp,ebpf过滤器连接到接口,因此如果激活 pinned-maps EBPF将保持与接口的连接,并且地图将在Suricata启动时保持可访问性。如果激活了xdp旁路,suricata将尝试在开始时打开锁定的地图。 flow_v4_tableflow_v6_table .如果它们存在,这意味着xdp过滤器仍然存在,Suricata将使用它们,而不是将xdp文件附加到接口。

因此,如果要重新加载xdp过滤器,则需要从 /sys/fs/bpf/ 开始手术前。

在这种情况下,您不使用旁路,这意味着使用的地图是从外部的管理。由于Suricata不知道它们的名称,因此需要指定要查找的映射的名称,该名称将用于检查是否存在xdp筛选器::

- interface: eth3
  pinned-maps: true
  pinned-maps-name: ipv4_drop
  xdp-filter-file: /usr/libexec/suricata/ebpf/xdp_filter.bpf

如果在ips模式下使用xdp旁路,停止Suricata将触发流量中断。要解决这个问题,提供的xdp过滤器 xdp_filter.bpf 如果设置为1,则包含将触发全局绕过的映射。你需要使用 pinned-maps 从这个功能中获益。

要使用它,您需要设置 #define USE_GLOBAL_BYPASS 1 (而不是0)在 xdp_filter.c 归档并重新生成ebpf代码,并将ebpf文件安装到正确的位置。如果你写信 1 作为密钥 0 然后xdp过滤器将切换到全局旁路模式。设置键 0 重视 0 把交通送到苏里塔。

必须在所有嗅探接口上激活开关。对于名为 eth0 全球交换地图将 /sys/fs/bpf/suricata-eth0-global_bypass .

19.4.11.1. 固定映射和eBPF过滤器

固定映射也可以与常规eBPF过滤器一起使用。主要的区别是,在Suricata停止之后,映射将不会持久化,因为它附加到一个套接字,而不是一个持久化的接口。

eBPF过滤器 filter.bpf 使用A ipv4_drop 包含要删除的IPv4地址集的映射。如果 pinned-maps 设置为 true 在接口配置中,映射将被固定在 /sys/fs/bpf/suricata-eth3-ipv4_drop .

然后你可以使用像 bpfctrl 管理映射中的IPv4地址。

19.4.12. 带Netronome的硬件旁路

Netronome卡支持硬件旁路。在这种情况下,EBPF代码在卡本身中运行。与驱动模式相比,这引入了一些架构上的差异,需要更新配置和EBPF过滤器。

在ebpf方面,从Linux4.19开始,不支持CPU映射和接口重定向,需要禁用这些功能。按体系结构,不应使用每CPU哈希,必须禁用它。为此,请编辑 ebpf/xdp_filter.c 然后做:

#define BUILD_CPUMAP        0
/* Increase CPUMAP_MAX_CPUS if ever you have more than 64 CPUs */
#define CPUMAP_MAX_CPUS     64

#define USE_PERCPU_HASH    0
#define GOT_TX_PEER    0

然后用创建bpf文件 make 并安装在预期位置。

Suricata配置相当简单,因为您需要激活硬件模式和 use-percpu-hash 期权在 af-packet 接口配置:

xdp-mode: hw
use-percpu-hash: no

负载平衡将在ebpf代码内的IP对上完成,因此使用 cluster_qm 因为集群类型是一个好主意:

cluster-type: cluster_qm

从Linux4.19开始,线程数必须是2的幂。所以设置 threads 的变量 af-packet 接口功率为2,在EBPF滤波器中,相应地设置以下变量:

#define RSS_QUEUE_NUMBERS   32

19.4.13. 获取有关旁路的实时信息

您可以通过stats事件和unix套接字获取有关绕过的信息。 iface-stat 将返回绕过的数据包数(当流超时时为其添加数据包)::

suricatasc -c "iface-stat enp94s0np0" | jq
{
  "message": {
    "pkts": 56529854964,
    "drop": 932328611,
    "bypassed": 1569467248,
    "invalid-checksums": 0
  },
  "return": "OK"
}

iface-bypassed-stats 命令将返回每个接口的IPv4和IPv6流表中的元素数:

# suricatasc
>>> iface-bypassed-stats
Success:
{
    "enp94s0np0": {
       "ipv4_fail": 0,
       "ipv4_maps_count": 2303,
       "ipv4_success": 4232,
       "ipv6_fail": 0,
       "ipv6_maps_count": 13131,
       "ipv6_success": 13500

    }
}

stats条目还包含 stats.flow_bypassed 具有本地和捕获字节和数据包计数器以及绕过和关闭的流计数器的对象:

{
  "local_pkts": 0,
  "local_bytes": 0,
  "local_capture_pkts": 20,
  "local_capture_bytes": 25000,
  "closed": 84,
  "pkts": 4799,
  "bytes": 2975133
}

local_pktslocal_bytes 用于Suricata绕过的流。这可能是因为使用了本地绕过,或者是因为捕获方法无法绕过更多的流。 pktsbytes 是来自捕获方法的计数器。由于超时时记帐,它们可能需要一些时间才能出现。 local_capture_pktslocal_capture_bytes 是Suricata在捕获方法有效地绕过流量之前看到的数据包的计数器。由于Suricata读取线程前面的缓冲区,几乎每个流都有一些缓冲区。