1

主要问题是我无法在网络接口 (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 安装,并且make命令正常工作(生成对象)。

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 笔记本电脑上运行

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

4

1 回答 1

2

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

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

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

于 2019-07-07T18:52:49.967 回答