1

如果我想编写一个过滤 icmp 数据包的 cBPF 程序,我可以通过执行tcpdump选项-ddwhich

将数据包匹配代码转储为 C 程序片段。

..见下面的例子

如何使用 eBPF 指令编写相同的程序?

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
/* ... */

/* From the example above: tcpdump -i lo icmp -dd */
struct sock_filter code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 1, 0x00000001 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
};

struct sock_fprog bpf = {
    .len = ARRAY_SIZE(code),
    .filter = code,
};

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0)
    /* ... bail out ... */

ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (ret < 0)
    /* ... bail out ... */

/* ... */
close(sock);
4

1 回答 1

1

您可以通过与您为 cBPF 程序所做的非常相似的方式传递 eBPF 指令:您可以有类似的东西

struct bpf_insn ebpf_code[] = {
{ 0xb7, 0, 0, 0, 0 },
{ 0x95, 0, 0, 0, 0 },
};

(注意指令的长度与 cBPF 的不同。)

但是,目前还没有工具可以像这样为您转储指令tcpdump -dd。这意味着您必须以另一种方式构建程序。

一种解决方案是自己编写 eBPF 指令。这很像汇编中的编程。你有一些内核中的 BPF 文档,或现有指令及其语法的列表

因为手动编写单独的 eBPF 指令并不有趣[需要引用],eBPF 的典型工作流程有些不同。clang/LLVM 有一个 eBPF 的后端,现在构建的大多数 eBPF 程序都依赖它。工作流程如下所示:

  1. 用 C 编写你的 BPF 程序。
  2. 使用 clang/LLVM 将其编译为目标文件 (ELF)。
  3. ip link使用工具或库( , tc filter, bpftool, bcc , libbpf , gobpf , ...)从目标文件中加载字节码。
  4. 将字节码注入内核并将其附加到挂钩(例如套接字)(这通常由相同的工具或库完成)。

内核有一堆用 C 编写的示例 BPF 程序。您可以看看其中一个是否可以适应您的需要。您需要实现的可能是:

  1. 检查您的数据包是否足够长以具有完整的以太网标头。
  2. 检查 ethertype 是否为 IPv4。
  3. 检查以太网标头之后,您的数据包是否足够长以包含完整的 IPv4 标头。
  4. 检查 IP 协议号是否为 ICMP。

然后返回与您想对该数据包执行的操作相关的操作(该值取决于您将程序附加到的挂钩、套接字/TC/XDP)。

于 2019-07-15T14:11:11.887 回答