主要问题是我无法在网络接口(XDP)中加载eBPF代码。
我正在尝试使用以下配置在Raspberry Pi 3上加载:
我正在使用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!
但是预期的结果是模块已正确加载到网络接口中。
此代码可在具有Kernel 4.15.0-54-generic的Ubuntu 18的笔记本电脑上工作
请问,有人知道如何正确配置树莓派以使其与此代码配合使用吗?
答案 0 :(得分:1)
您的内核可能未在BPF支持下进行编译(我的4.15 Raspbian并非如此)。您可以按照以下步骤进行检查:
sudo modprobe configs
zgrep -E "(BPF|XDP)" /proc/config.gz
CONFIG_BPF
和CONFIG_BPF_SYSCALL
均应启用。如果不是,那么您需要启用这些配置重新编译内核。