主要问题是我无法在网络接口 (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 笔记本电脑上运行
拜托,有人知道如何正确配置树莓派以使用此代码吗?