【问题标题】:Fail to load eBPF/XDP code on Raspberry Pi无法在 Raspberry Pi 上加载 eBPF/XDP 代码
【发布时间】:2019-07-07 18:08:05
【问题描述】:

主要问题是我无法在网络接口 (XDP) 中加载 eBPF 代码。

我正在尝试使用以下配置在 Raspberry Pi 3 上加载:

  • Raspbian GNU/Linux 10 (Buster)
  • 内核 4.19.50-v7+

我使用ip命令如下:

$ sudo ip -force link set dev wlan0 xdp obj portfilter.o sec filter

在此之前,我通过 apt 执行了 clang-7 安装,并且 ma​​ke 命令正常工作(生成对象)。

Makefile源码如下:

# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)

LLVM_VERSION ?= -7 #update with correct LLVM / clang version
LLVM := $(shell clang$(LLVM_VERSION) --version)
CLANG_FLAGS ?= -W -Wall \
    -Wno-compare-distinct-pointer-types

SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c,%.o,$(SRCS))
Q ?= @

INCLUDE_DIRS ?= -Iheaders/

%.o: %.c
    @echo "\tLLVM CC $@"
    $(Q) clang$(LLVM_VERSION) $(INCLUDE_DIRS) -O2 -emit-llvm -c $< $(CLANG_FLAGS) -o $(patsubst %.o,%.llvm,$@)
    $(Q) llc$(LLVM_VERSION) -march=bpf -filetype=obj -o $@ $(patsubst %.o,%.llvm,$@)
    $(Q) rm $(patsubst %.o,%.llvm,$@)

ifeq ($(LLVM),)
all:
    $(warning Install LLVM to compile BPF sources)
else
all: $(OBJS)
endif

clean:
    rm -f *.llvm
    rm -f *.o

.PHONY: all clean

以及portfilter.c预计要加载的源代码:

#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <linux/bpf.h>
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include "bpf_endian.h"
#include "bpf_helpers.h"

/* 0x3FFF mask to check for fragment offset field */
#define IP_FRAGMENTED 65343

/* Port number to be dropped */
#define PORT_DROP 80

static __always_inline int process_packet(struct xdp_md *ctx, __u64 off){

    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct iphdr *iph;
    struct tcphdr *tcp;
    __u16 payload_len;
    __u8 protocol;

    iph = data + off;
    if (iph + 1 > data_end)
        return XDP_PASS;
    if (iph->ihl != 5)
        return XDP_PASS;

    protocol = iph->protocol;
    payload_len = bpf_ntohs(iph->tot_len);
    off += sizeof(struct iphdr);

    /* do not support fragmented packets as L4 headers may be missing */
    if (iph->frag_off & IP_FRAGMENTED)
        return XDP_PASS;

    if (protocol == IPPROTO_TCP) {
        tcp = data + off;
        if(tcp + 1 > data_end)
            return XDP_PASS;

        /* Drop if using port PORT_DROP */
        if(tcp->source == bpf_htons(PORT_DROP) || tcp->dest == bpf_htons(PORT_DROP))
            return XDP_DROP;
        else
            return XDP_PASS;

    } else if (protocol == IPPROTO_UDP) {
        return XDP_PASS;
    }

    return XDP_PASS;
}


SEC("filter")
int pfilter(struct xdp_md *ctx){

    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;
    __u32 eth_proto;
    __u32 nh_off;

    nh_off = sizeof(struct ethhdr);
    if (data + nh_off > data_end)
        return XDP_PASS;
    eth_proto = eth->h_proto;

    /* demo program only accepts ipv4 packets */
    if (eth_proto == bpf_htons(ETH_P_IP))
        return process_packet(ctx, nh_off);
    else
        return XDP_PASS;
}

很遗憾,ip命令后的实际输出如下:

mkdir /sys/fs/bpf failed: Operation not permitted
Continuing without mounted eBPF fs. Too old kernel?

Prog section 'filter' rejected: Function not implemented (38)!
 - Type:         6
 - Instructions: 38 (0 over limit)
 - License:      

Verifier analysis:

Error fetching program/map!

但预期的结果是模块在网络接口中正确加载。

此代码在带有内核 4.15.0-54-generic 的 Ubuntu 18 笔记本电脑上运行

请,有人知道如何正确配置树莓派以使用此代码吗?

【问题讨论】:

  • 以防万一,您是否检查过您的内核是否支持 BPF? grep BPF /usr/src/linux-headers-$(uname -r)/.config 带给你什么?
  • (或者您的 .config 文件可能不在我提供的位置——只需按照 pchaigno 的说明进行操作即可:))
  • 确认需要重新编译内核。 .config 缺少 eBPF 配置

标签: c clang bpf ebpf


【解决方案1】:

您的内核可能没有使用 BPF 支持编译(我的 4.15 Raspbian 不是)。您可以通过以下步骤进行检查:

sudo modprobe configs
zgrep -E "(BPF|XDP)" /proc/config.gz

CONFIG_BPFCONFIG_BPF_SYSCALL 都应该启用。如果不是,那么您需要在启用这些配置的情况下重新编译您的内核

【讨论】:

  • Pi 上没有zgrep
  • 有。我总是忘记这些命令。已修复,谢谢!
  • 确认@pchaigno 检查 .config CONFIG_BPF = y 但 # CONFIG_BPF_SYSCALL 未设置 然后执行内核重新编译: $ make menuconfig 配置以下内容:1) 网络支持 2) 网络选项 2.1) XDP 套接字 2.2) 基于 BPF 的包过滤框架 2.3) 启用 BPF Just In Time 编译器 然后还配置:1) 常规设置 1.1) 启用 bpf( ) 系统调用 1.2) 永久启用 BPF JIT 并移除 BPF 解释器 (Y)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2017-12-13
相关资源
最近更新 更多