Не удалось загрузить код eBPF/XDP на Raspberry Pi

Основная проблема в том, что я не могу загрузить код eBPF в сетевой интерфейс (XDP).

Я пытаюсь загрузить Raspberry Pi 3 со следующей конфигурацией:

  • Raspbian GNU/Linux 10 (Бастер)
  • Ядро 4.19.50-v7+

Я использую команду ip следующим образом:

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

До этого я выполнял установку clang-7 через apt и команда 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!

Но ожидаемый результат — корректная загрузка модуля в сетевой интерфейс.

Этот код работает на ноутбуке с Ubuntu 18 и ядром 4.15.0-54-generic

Пожалуйста, кто-нибудь знает, как правильно настроить raspberry pi для работы с этим кодом?


person Gustavo Bertoli    schedule 07.07.2019    source источник
comment
На всякий случай, вы проверили, что ваше ядро ​​поддерживает BPF? Что вам дает grep BPF /usr/src/linux-headers-$(uname -r)/.config?   -  person Qeole    schedule 07.07.2019
comment
(Или, возможно, ваш файл .config находится не в том месте, которое я указал - вместо этого просто следуйте инструкциям pchaigno :))   -  person Qeole    schedule 07.07.2019
comment
Подтверждено, что требуется перекомпиляция ядра. В .config отсутствует конфигурация eBPF   -  person Gustavo Bertoli    schedule 07.07.2019


Ответы (1)


Ваше ядро, скорее всего, не скомпилировано с поддержкой BPF (мой Raspbian 4.15 — нет). Вы можете проверить это, выполнив следующие действия:

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

И CONFIG_BPF, и CONFIG_BPF_SYSCALL должны быть включены. Если это не так, вам нужно перекомпилировать ядро с включенными этими настройками.

person pchaigno    schedule 07.07.2019
comment
Нет zgrep на Пи? - person Qeole; 07.07.2019
comment
Есть. Я всегда забываю об этих командах. Исправлено, спасибо! - person pchaigno; 07.07.2019
comment
Проверка @pchaigno .config подтвердила, что CONFIG_BPF = y, но # CONFIG_BPF_SYSCALL не установлен Затем была выполнена перекомпиляция ядра: $ make menuconfig Настройте следующее: 1) Поддержка сети ‹ENTER› 2) Параметры сети ‹ENTER› 2.1) Сокеты XDP 2.2 ) Фреймворк фильтрации пакетов на основе BPF ‹Y› 2.3) включить компилятор BPF Just In Time ‹Y› ‹Сохранить› ‹Загрузить› Затем также настроить: 1) Общие настройки ‹ENTER› 1.1) Включить системный вызов bpf() ‹Y› 1.2) Постоянно включить BPF JIT и удалить интерпретатор BPF (Y) ‹Сохранить› ‹Загрузить› - person Gustavo Bertoli; 08.07.2019