使用 ip 命令安装 XDP 程序时出错

error when install XDP program using ip command

我正在尝试在我的网络驱动程序上安装 XDP 程序,但我收到错误消息 ELF 在指向第 4 节的条目 0 中包含非 {map,call} 相关的 relo 数据!编译器错误?!获取 program/map!

时出错

我正在尝试的代码 运行 :

#define KBUILD_MODNAME "filter"
#include <arpa/inet.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>

static int (*bpf_trace_printk)(const char *fmt,...) = (void *)BPF_FUNC_trace_printk;

int udpfilter(struct xdp_md *ctx) {
    bpf_trace_printk("got a packet\n");
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    if ((void*)eth + sizeof(*eth) <= data_end) {
       struct iphdr *ip = data + sizeof(*eth);
       if ((void*)ip + sizeof(*ip) <= data_end) {
         if (ip->protocol == IPPROTO_UDP) {
            struct udphdr *udp = (void*)ip + sizeof(*ip);
            if ((void*)udp + sizeof(*udp) <= data_end) {
               if (udp->dest == ntohs(7999)) {
                  bpf_trace_printk("udp port 7999\n");
                  udp->dest = ntohs(7998);
              }
            }
          }
        }
      }
   return XDP_PASS;
   }

编译命令:clang -O2 -g -Wall -target bpf -c filter.c -o filter.o 就可以了!

和我用来安装的命令:ip link set enp0s3 xdpgeneric obj filter.o 然后我得到上面的错误。

我不确定这条消息是什么意思,我是不是漏掉了什么?

获取错误 program/map!

您不必在编译时包含其他文件中提到的这个目录。此错误是由于 ip 未在 ELF 文件中找到预期的部分。参见 ip-link man page:

If no section option is passed, then the default section name ("prog") will be assumed, otherwise the provided section name will be used.

所以你有两个解决方案:

  1. 将函数放在 prog ELF 部分:

    __attribute__((section("prog"), used))
    int udpfilter(struct xdp_md *ctx) {
            [...]
    }
    
  2. 加载程序时使用section选项。默认情况下,clang 将您的代码放入 .text 部分:

    # ip link set enp0s3 xdpgeneric obj filter.o sec .text
    

ELF 在指向第 4 节的条目 0 中包含与{map,call} 无关的 relo 数据!编译器错误?!

如前所述,这是您使用 bpf_trace_prink() 造成的。您不能简单地将格式字符串直接传递给帮助程序,因为 clang 会将其放置在特定的 ELF 部分并将 at 标记为重定位数据,但加载程序(在本例中为 ip)将不知道如何处理它.当然,您必须至少向它传递第二个参数,以提供格式字符串的大小。有关详细信息,请参阅 its documentation

请注意,我们经常可以看到像这样为该助手定义的包装器:

#define bpf_printk(fmt, ...)                    \
({                                              \
    char ____fmt[] = fmt;                       \
    bpf_trace_printk(____fmt, sizeof(____fmt),  \
             ##__VA_ARGS__);                    \
})

字符串的声明方式不会触发任何重定位,之后使用起来更方便,你可以直接bpf_printk("got a packet\n");