update 43 for xdp test

This commit is contained in:
yunwei37
2024-11-30 02:10:57 +00:00
parent cad85ca032
commit 1696b2b559
10 changed files with 534 additions and 4 deletions

View File

@@ -393,7 +393,7 @@ Assuming you have a user-space application or a tool to load and attach the eBPF
**Sample Output:**
```bash
# sudo ./load_kfunc
# sudo ./kfunc
BPF program loaded and attached successfully. Press Ctrl-C to exit.
```

View File

@@ -393,7 +393,7 @@ int handle_kprobe(struct pt_regs *ctx)
**示例输出:**
```bash
# sudo ./load_kfunc
# sudo ./kfunc
BPF 程序已加载并成功附加。按 Ctrl-C 退出。
```

3
src/46-xdp-test/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/.output
/bootstrap
xdp-pktgen

141
src/46-xdp-test/Makefile Normal file
View File

@@ -0,0 +1,141 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../third_party/libbpf/src)
BPFTOOL_SRC := $(abspath ../third_party/bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
LIBBLAZESYM_SRC := $(abspath ../third_party/blazesym/)
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a)
LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h)
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../third_party/vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../third_party/libbpf/include/uapi -I$(dir $(VMLINUX))
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
APPS = xdp-pktgen # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall
CARGO ?= $(shell which cargo)
ifeq ($(strip $(CARGO)),)
BZS_APPS :=
else
BZS_APPS := # profile
APPS += $(BZS_APPS)
# Required by libblazesym
ALL_LDFLAGS += -lrt -ldl -lpthread -lm
endif
# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
# architecture-specific dirs will be "missing" on some architectures/distros -
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
# sys/cdefs.h etc. might be missing.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' \
"$(1)" \
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
"$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif
define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef
$(call allow-override,CC,$(CROSS_COMPILE)cc)
$(call allow-override,LD,$(CROSS_COMPILE)ld)
.PHONY: all
all: $(APPS)
.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS)
$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
# Build libbpf
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
$(call msg,LIB,$@)
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
INCLUDEDIR= LIBDIR= UAPIDIR= \
install
# Build bpftool
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
$(call msg,BPFTOOL,$@)
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap
$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@
$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@
# Build BPF code
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
# Generate BPF skeletons
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@
# Build user-space code
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER)
$(BZS_APPS): $(LIBBLAZESYM_OBJ)
# Build application binary
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@
# delete failed targets
.DELETE_ON_ERROR:
# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:

44
src/46-xdp-test/README.md Normal file
View File

@@ -0,0 +1,44 @@
# xdp-pktgen: xdp based packet generator
This is a simple xdp based packet generator.
## **How to use**
clone the repo, you can update the git submodule with following commands:
```sh
git submodule update --init --recursive
```
### **3. Install dependencies**
For dependencies, it varies from distribution to distribution. You can refer to shell.nix and dockerfile for installation.
On Ubuntu, you may run `make install` or
```sh
sudo apt-get install -y --no-install-recommends \
libelf1 libelf-dev zlib1g-dev \
make clang llvm
```
to install dependencies.
### **4. Build the project**
To build the project, run the following command:
```sh
make build
```
This will compile your code and create the necessary binaries. You can you the `Github Code space` or `Github Action` to build the project as well.
### ***Run the Project***
You can run the binary with:
```console
sudo ./xdp-pktgen
```

View File

@@ -0,0 +1,44 @@
# xdp-pktgen: xdp based packet generator
This is a simple xdp based packet generator.
## **How to use**
clone the repo, you can update the git submodule with following commands:
```sh
git submodule update --init --recursive
```
### **3. Install dependencies**
For dependencies, it varies from distribution to distribution. You can refer to shell.nix and dockerfile for installation.
On Ubuntu, you may run `make install` or
```sh
sudo apt-get install -y --no-install-recommends \
libelf1 libelf-dev zlib1g-dev \
make clang llvm
```
to install dependencies.
### **4. Build the project**
To build the project, run the following command:
```sh
make build
```
This will compile your code and create the necessary binaries. You can you the `Github Code space` or `Github Action` to build the project as well.
### ***Run the Project***
You can run the binary with:
```console
sudo ./xdp-pktgen
```

View File

@@ -0,0 +1,114 @@
#ifndef TEST_UDP_PKT_H
#define TEST_UDP_PKT_H
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_link.h>
#include <linux/ipv6.h>
#include <linux/ip.h>
#include <linux/in6.h>
#include <linux/in.h>
#include <linux/udp.h>
#include <arpa/inet.h> // For inet_addr()
#include <string.h> // For memcpy, memset
#include <bpf/bpf_endian.h>
#include <linux/netdev.h>
#include <assert.h>
#define PORT 9876
#define SERVER_IP "127.0.0.1"
// Define packet structures first
struct test_udp_packet {
struct ethhdr eth;
struct ipv6hdr iph;
struct udphdr udp;
__u8 payload[64 - sizeof(struct udphdr) - sizeof(struct ethhdr) - sizeof(struct ipv6hdr)];
} __attribute__((packed));
struct test_udp_packet_v4 {
struct ethhdr eth;
struct iphdr iph;
struct udphdr udp;
uint8_t payload[64 - sizeof(struct udphdr) - sizeof(struct ethhdr) - sizeof(struct iphdr)];
} __attribute__((packed));
// Helper function declarations
static uint16_t ip_checksum(void *vdata, size_t length);
static __be16 __calc_udp_cksum(const struct test_udp_packet *pkt);
// Helper function implementations
static uint16_t ip_checksum(void *vdata, size_t length) {
char *data = vdata;
uint64_t acc = 0xffff;
for (size_t i = 0; i + 1 < length; i += 2) {
uint16_t word;
memcpy(&word, data + i, 2);
acc += ntohs(word);
if (acc > 0xffff) {
acc -= 0xffff;
}
}
if (length & 1) {
uint16_t word = 0;
memcpy(&word, data + length - 1, 1);
acc += ntohs(word);
if (acc > 0xffff) {
acc -= 0xffff;
}
}
return htons(~acc);
}
static __be16 __calc_udp_cksum(const struct test_udp_packet *pkt) {
__u32 chksum = pkt->iph.nexthdr + bpf_ntohs(pkt->iph.payload_len);
int i;
for (i = 0; i < 8; i++) {
chksum += bpf_ntohs(pkt->iph.saddr.s6_addr16[i]);
chksum += bpf_ntohs(pkt->iph.daddr.s6_addr16[i]);
}
chksum += bpf_ntohs(pkt->udp.source);
chksum += bpf_ntohs(pkt->udp.dest);
chksum += bpf_ntohs(pkt->udp.len);
while (chksum >> 16)
chksum = (chksum & 0xFFFF) + (chksum >> 16);
return bpf_htons(~chksum);
}
static struct test_udp_packet_v4 create_test_udp_packet_v4(void) {
struct test_udp_packet_v4 pkt = {0};
// Ethernet header
pkt.eth.h_proto = htons(ETH_P_IP);
memcpy(pkt.eth.h_dest, (const unsigned char[]){0xb8, 0x3f, 0xd2, 0x2a, 0xe5, 0x11}, sizeof(pkt.eth.h_dest));
memcpy(pkt.eth.h_source, (const unsigned char[]){0xb8, 0x3f, 0xd2, 0x2a, 0xe7, 0x69}, sizeof(pkt.eth.h_source));
// IPv4 header
pkt.iph.version = 4;
pkt.iph.ihl = 5;
pkt.iph.tot_len = htons(sizeof(struct test_udp_packet_v4) - sizeof(struct ethhdr));
pkt.iph.ttl = 64; // default TTL
pkt.iph.protocol = IPPROTO_UDP;
pkt.iph.saddr = inet_addr(SERVER_IP);
pkt.iph.daddr = inet_addr(SERVER_IP);
pkt.iph.check = ip_checksum(&pkt.iph, sizeof(struct iphdr));
// UDP header
pkt.udp.source = htons(12345);
pkt.udp.dest = htons(PORT);
pkt.udp.len = htons(sizeof(struct udphdr) + sizeof(pkt.payload));
pkt.udp.check = 0; // Optional for IPv4
// Payload
memset(pkt.payload, 0x42, sizeof(pkt.payload));
return pkt;
}
#endif // TEST_UDP_PKT_H

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: MIT */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
char _license[] SEC("license") = "GPL";
SEC("xdp")
int xdp_redirect_notouch(struct xdp_md *ctx)
{
return XDP_TX;
}

View File

@@ -0,0 +1,172 @@
/* SPDX-License-Identifier: MIT */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <bpf/bpf.h>
#include <bpf/bpf_endian.h>
#include <bpf/libbpf.h>
#include <linux/bpf.h>
#include <linux/types.h>
#include <linux/if_link.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ipv6.h>
#include <linux/in6.h>
#include "xdp-pktgen.skel.h"
#include "test_udp_pkt.h"
#ifndef BPF_F_TEST_XDP_LIVE_FRAMES
#define BPF_F_TEST_XDP_LIVE_FRAMES (1U << 1)
#endif
static volatile bool exiting = false;
static void sig_handler(int sig)
{
exiting = true;
}
struct config {
int ifindex;
int xdp_flags;
int repeat;
int batch_size;
};
struct config cfg = {
.ifindex = 6,
.repeat = 1 << 20,
.batch_size = 0,
};
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
return vfprintf(stderr, format, args);
}
static int run_prog(int run_prog_fd, int count)
{
// struct test_udp_packet pkt_udp = create_test_udp_packet_v6();
char* pkt_file = NULL;
unsigned char pkt_file_buffer[1024];
int size = 0;
if ((pkt_file = getenv("PKTGEN_FILE")) != NULL) {
FILE* file = fopen(pkt_file, "r");
if (file == NULL) {
printf("Error opening file\n");
return -1;
}
// read the the file length of data into the buffer
size = fread(pkt_file_buffer, 1, 1024, file);
fclose(file);
} else {
struct test_udp_packet_v4 pkt_udp = create_test_udp_packet_v4();
size = sizeof(pkt_udp);
memcpy(pkt_file_buffer, &pkt_udp, size);
}
struct xdp_md ctx_in = {
.data_end = size,
.ingress_ifindex = cfg.ifindex
};
// struct xdp_md ctx_in_array[64];
// for (int i = 0; i < 64; i++) {
// ctx_in_array[i].data_end = sizeof(pkt_udp);
// ctx_in_array[i].ingress_ifindex = cfg.ifindex;
// }
printf("pkt size: %ld\n", size);
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
.data_in = pkt_file_buffer,
.data_size_in = size,
.ctx_in = &ctx_in,
.ctx_size_in = sizeof(ctx_in),
.repeat = cfg.repeat,
.flags = BPF_F_TEST_XDP_LIVE_FRAMES,
.batch_size = cfg.batch_size,
.cpu = 0,
);
__u64 iterations = 0;
cpu_set_t cpu_cores;
int err;
CPU_ZERO(&cpu_cores);
CPU_SET(0, &cpu_cores);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_cores);
do {
err = bpf_prog_test_run_opts(run_prog_fd, &opts);
if (err)
return -errno;
iterations += opts.repeat;
} while ((count == 0 || iterations < count) && !exiting);
return 0;
}
static int probe_kernel_support(int run_prog_fd)
{
int err = run_prog(run_prog_fd, 1);
if (err == -EOPNOTSUPP) {
printf("BPF_PROG_RUN with batch size support is missing from libbpf.\n");
} else if (err == -EINVAL) {
err = -EOPNOTSUPP;
printf("Kernel doesn't support live packet mode for XDP BPF_PROG_RUN.\n");
} else if (err) {
printf("Error probing kernel support: %s\n", strerror(-err));
} else {
printf("Kernel supports live packet mode for XDP BPF_PROG_RUN.\n");
}
return err;
}
int main()
{
struct xdp_pktgen_bpf *skel = NULL;
int err = 0;
__u32 key = 0;
/* Set up libbpf errors and debug info callback */
libbpf_set_print(libbpf_print_fn);
/* Cleaner handling of Ctrl-C */
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
skel = xdp_pktgen_bpf__open();
if (!skel) {
err = -errno;
printf("Couldn't open XDP program: %s\n", strerror(-err));
goto out;
}
err = xdp_pktgen_bpf__load(skel);
if (err)
goto out;
int run_prog_fd = bpf_program__fd(skel->progs.xdp_redirect_notouch);
// probe kernel support for BPF_PROG_RUN
err = probe_kernel_support(run_prog_fd);
if (err)
return err;
err = run_prog(run_prog_fd, 0);
if (err) {
printf("run xdp program error: %d\n", err);
}
out:
xdp_pktgen_bpf__destroy(skel);
return err;
}

View File

@@ -211,8 +211,8 @@ def generate_toc_cn(base_dir, project_root):
return toc
# Example usage
base_directory = "/root/bpf-developer-tutorial/src/" # Replace with the actual base directory
project_root = "/root/bpf-developer-tutorial/" # The root of the project
base_directory = "src/" # Replace with the actual base directory
project_root = "./" # The root of the project
toc_output = generate_toc(base_directory, project_root)
# toc_output = generate_toc_cn(base_directory, project_root)
# Output the TOC