diff --git a/backends/ebpf/CMakeLists.txt b/backends/ebpf/CMakeLists.txt index a2d126fc7f..215e203c0a 100644 --- a/backends/ebpf/CMakeLists.txt +++ b/backends/ebpf/CMakeLists.txt @@ -21,9 +21,9 @@ if(NOT APPLE) set(FETCHCONTENT_QUIET OFF) fetchcontent_declare( bpfrepo - URL https://github.com/libbpf/libbpf/archive/refs/tags/v1.4.1.tar.gz - URL_HASH SHA256=cc01a3a05d25e5978c20be7656f14eb8b6fcb120bb1c7e8041e497814fc273cb - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/runtime/contrib/libbpf + URL https://github.com/libbpf/bpftool/releases/download/v7.5.0/bpftool-libbpf-v7.5.0-sources.tar.gz + # URL_HASH SHA256=cc01a3a05d25e5978c20be7656f14eb8b6fcb120bb1c7e8041e497814fc273cb + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/runtime/contrib/bpftool USES_TERMINAL_DOWNLOAD TRUE GIT_PROGRESS TRUE DOWNLOAD_EXTRACT_TIMESTAMP TRUE @@ -31,7 +31,7 @@ if(NOT APPLE) fetchcontent_makeavailable(bpfrepo) set(FETCHCONTENT_QUIET ${FETCHCONTENT_QUIET_PREV}) # Check if we have already built the libbpf library. - find_library(LIBBPF NAMES bpf HINTS "${CMAKE_CURRENT_SOURCE_DIR}/runtime/usr/lib64/") + find_library(LIBBPF NAMES bpf HINTS "${CMAKE_CURRENT_SOURCE_DIR}/runtime/install/libbpf/") if (NOT LIBBPF) message("Building libbpf...") execute_process( diff --git a/backends/ebpf/build_libbpf b/backends/ebpf/build_libbpf index 8ff46ad598..f354177033 100755 --- a/backends/ebpf/build_libbpf +++ b/backends/ebpf/build_libbpf @@ -10,7 +10,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""" This programs builds a libbpf static library and places it in the runtime +""" This programs builds a bpftool binary and the libbpf static library and places it in the runtime folder. The library and its headers are required by the kernel target. """ @@ -31,21 +31,24 @@ def main() -> int: level=logging.WARN, filemode="w", ) - libbpf_dir = FILE_DIR.joinpath("runtime/contrib/libbpf") - libbpf_src_dir = libbpf_dir.joinpath("src") - libbpf_build_dir = libbpf_src_dir.joinpath("build") - libbpf_target_dir = FILE_DIR.joinpath("runtime") - # Create the libbpf build directory - testutils.check_and_create_dir(libbpf_build_dir) - # Build libbpf - mk_cmd = f"make -C {libbpf_src_dir} install " + bpftool_dir = FILE_DIR.joinpath("runtime/contrib/bpftool") + bpftool_src_dir = bpftool_dir.joinpath("src") + bpftool_build_dir = bpftool_src_dir.joinpath("build") + bpftool_target_dir = FILE_DIR.joinpath("runtime/install/") + bpftool_target_dir.mkdir(parents=True, exist_ok=True) + # Create the bpftool build directory + testutils.check_and_create_dir(bpftool_build_dir) + # Build bpftool + mk_cmd = f"make -C {bpftool_src_dir} install " + mk_cmd += f"OUTPUT={bpftool_target_dir}/ " mk_cmd += "BUILD_STATIC_ONLY=y " - mk_cmd += f"OBJDIR={libbpf_build_dir} " - mk_cmd += f"DESTDIR={libbpf_target_dir} " + mk_cmd += f"OBJDIR={bpftool_build_dir} " + mk_cmd += f"DESTDIR={bpftool_target_dir} " mk_cmd += "EXTRA_CFLAGS=-fPIE" + print(mk_cmd) result = testutils.exec_process(args=mk_cmd) if result.returncode != testutils.SUCCESS: - testutils.log.error("Could not build libbpf") + testutils.log.error("Could not build bpftool") return result.returncode diff --git a/backends/ebpf/run-ebpf-test.py b/backends/ebpf/run-ebpf-test.py index 6757042a4b..f69b9aaf85 100755 --- a/backends/ebpf/run-ebpf-test.py +++ b/backends/ebpf/run-ebpf-test.py @@ -123,9 +123,9 @@ def __init__(self): self.replace = False # Replace previous outputs. self.target = "test" # The name of the target compiler. # Actual location of the test framework. - self.testdir = str(FILE_DIR) + self.testdir = FILE_DIR # The location of the eBPF runtime, some targets may overwrite this. - self.runtimedir = str(FILE_DIR.joinpath("runtime")) + self.runtimedir = FILE_DIR.joinpath("runtime") self.extern = "" # Path to C file with extern definition. diff --git a/backends/ebpf/runtime/.gitignore b/backends/ebpf/runtime/.gitignore new file mode 100644 index 0000000000..7c32f55981 --- /dev/null +++ b/backends/ebpf/runtime/.gitignore @@ -0,0 +1 @@ +install diff --git a/backends/ebpf/runtime/contrib/.gitignore b/backends/ebpf/runtime/contrib/.gitignore index cc3ea8dd44..d30b12ae8b 100644 --- a/backends/ebpf/runtime/contrib/.gitignore +++ b/backends/ebpf/runtime/contrib/.gitignore @@ -1 +1,2 @@ libbpf +bpftool diff --git a/backends/ebpf/runtime/ebpf_kernel.h b/backends/ebpf/runtime/ebpf_kernel.h index 6813c0affc..d3529be5fc 100644 --- a/backends/ebpf/runtime/ebpf_kernel.h +++ b/backends/ebpf/runtime/ebpf_kernel.h @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ - /// This file contains all functions and definitions necessary for the kernel target C /// code to compile. It must be included with any file generated by the p4c-ebpf kernel /// compiler. @@ -22,8 +21,7 @@ limitations under the License. #define BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_ #include "ebpf_common.h" - -#include // definitions for bpf_ntohs etc... +#include "install/libbpf/include/bpf/bpf_endian.h" // definitions for bpf_ntohs etc... #undef htonl #undef htons @@ -35,158 +33,82 @@ limitations under the License. #define bpf_htonll(x) htonll(x) #endif -#define load_byte(data, b) (*(((u8*)(data)) + (b))) -#define load_half(data, b) bpf_ntohs(*(u16 *)((u8*)(data) + (b))) -#define load_word(data, b) bpf_ntohl(*(u32 *)((u8*)(data) + (b))) -#define load_dword(data, b) bpf_be64_to_cpu(*(u64 *)((u8*)(data) + (b))) +#define load_byte(data, b) (*(((u8 *)(data)) + (b))) +#define load_half(data, b) bpf_ntohs(*(u16 *)((u8 *)(data) + (b))) +#define load_word(data, b) bpf_ntohl(*(u32 *)((u8 *)(data) + (b))) +#define load_dword(data, b) bpf_be64_to_cpu(*(u64 *)((u8 *)(data) + (b))) /// If we operate in user space we only need to include bpf.h and /// define the userspace API macros. /// For kernel programs we need to specify a list of kernel helpers. These are -/// taken from here: https://github.com/torvalds/linux/blob/master/tools/lib/bpf/bpf_helpers.h +/// taken from here: https://github.com/libbpf/libbpf/blob/master/src/bpf_helpers.h -#ifdef CONTROL_PLANE // BEGIN EBPF USER SPACE DEFINITIONS +#ifdef CONTROL_PLANE // BEGIN EBPF USER SPACE DEFINITIONS -#include // bpf_obj_get/pin, bpf_map_update_elem +#include "install/libbpf/include/bpf/bpf.h" // bpf_obj_get/pin, bpf_map_update_elem -#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags)\ +#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags) \ bpf_map_update_elem(index, key, value, flags) #define BPF_OBJ_PIN(table, name) bpf_obj_pin(table, name) #define BPF_OBJ_GET(name) bpf_obj_get(name) -#else // BEGIN EBPF KERNEL DEFINITIONS +#else // BEGIN EBPF KERNEL DEFINITIONS -#include // TC_ACT_OK, TC_ACT_SHOT -#include "linux/bpf.h" // types, and general bpf definitions +#include "contrib/bpftool/include/uapi/linux/bpf.h" // BPF_ANY, +#include "contrib/bpftool/include/uapi/linux/pkt_cls.h" // TC_ACT_OK, TC_ACT_SHOT // This file contains the definitions of all the kernel bpf essentials -#include - -/// A helper structure used by an eBPF C program -/// to describe map attributes for the elf_bpf loader -/// FIXME: We only need this because we are loading with iproute2 -struct bpf_elf_map { - __u32 type; - __u32 size_key; - __u32 size_value; - __u32 max_elem; - __u32 flags; - __u32 id; - __u32 pinning; - __u32 inner_id; - __u32 inner_idx; -}; +#include "install/libbpf/include/bpf/bpf_helpers.h" /// Simple descriptor which replaces the kernel sk_buff structure. #define SK_BUFF struct __sk_buff -/// From iproute2, annotate table with BTF which allows to read types at runtime. -#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ - struct ____btf_map_##name { \ - type_key key; \ - type_val value; \ - }; \ - struct ____btf_map_##name \ - __attribute__ ((section(".maps." #name), used)) \ - ____btf_map_##name = {}; - #define REGISTER_START() -#ifndef BTF -/// Note: pinning exports the table name globally, do not remove. -#define REGISTER_TABLE(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \ -struct bpf_elf_map SEC("maps") NAME = { \ - .type = TYPE, \ - .size_key = sizeof(KEY_TYPE), \ - .size_value = sizeof(VALUE_TYPE), \ - .max_elem = MAX_ENTRIES, \ - .pinning = 2, \ - .flags = 0, \ -}; -#define REGISTER_TABLE_INNER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, ID, INNER_IDX) \ -struct bpf_elf_map SEC("maps") NAME = { \ - .type = TYPE, \ - .size_key = sizeof(KEY_TYPE), \ - .size_value = sizeof(VALUE_TYPE), \ - .max_elem = MAX_ENTRIES, \ - .pinning = 2, \ - .flags = 0, \ - .id = ID, \ - .inner_idx = INNER_IDX, \ -}; -#define REGISTER_TABLE_OUTER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, INNER_ID, INNER_NAME) \ -struct bpf_elf_map SEC("maps") NAME = { \ - .type = TYPE, \ - .size_key = sizeof(KEY_TYPE), \ - .size_value = sizeof(VALUE_TYPE), \ - .max_elem = MAX_ENTRIES, \ - .pinning = 2, \ - .flags = 0, \ - .inner_id = INNER_ID, \ -}; -#define REGISTER_TABLE_FLAGS(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, FLAGS) \ -struct bpf_elf_map SEC("maps") NAME = { \ - .type = TYPE, \ - .size_key = sizeof(KEY_TYPE), \ - .size_value = sizeof(VALUE_TYPE), \ - .max_elem = MAX_ENTRIES, \ - .pinning = 2, \ - .flags = FLAGS, \ -}; -#else #define REGISTER_TABLE(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \ -struct { \ - __uint(type, TYPE); \ - KEY_TYPE *key; \ - VALUE_TYPE *value; \ - __uint(max_entries, MAX_ENTRIES); \ - __uint(pinning, LIBBPF_PIN_BY_NAME); \ -} NAME SEC(".maps"); + struct { \ + __uint(type, TYPE); \ + KEY_TYPE *key; \ + VALUE_TYPE *value; \ + __uint(max_entries, MAX_ENTRIES); \ + } NAME SEC(".maps"); #define REGISTER_TABLE_FLAGS(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, FLAGS) \ -struct { \ - __uint(type, TYPE); \ - KEY_TYPE *key; \ - VALUE_TYPE *value; \ - __uint(max_entries, MAX_ENTRIES); \ - __uint(pinning, LIBBPF_PIN_BY_NAME); \ - __uint(map_flags, FLAGS); \ -} NAME SEC(".maps"); + struct { \ + __uint(type, TYPE); \ + KEY_TYPE *key; \ + VALUE_TYPE *value; \ + __uint(max_entries, MAX_ENTRIES); \ + __uint(map_flags, FLAGS); \ + } NAME SEC(".maps"); #define REGISTER_TABLE_INNER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, ID, INNER_IDX) \ -struct NAME { \ - __uint(type, TYPE); \ - KEY_TYPE *key; \ - VALUE_TYPE *value; \ - __uint(max_entries, MAX_ENTRIES); \ -} NAME SEC(".maps"); + struct NAME { \ + __uint(type, TYPE); \ + KEY_TYPE *key; \ + VALUE_TYPE *value; \ + __uint(max_entries, MAX_ENTRIES); \ + } NAME SEC(".maps"); #define REGISTER_TABLE_OUTER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, INNER_ID, INNER_NAME) \ -struct { \ - __uint(type, TYPE); \ - KEY_TYPE *key; \ - VALUE_TYPE *value; \ - __uint(max_entries, MAX_ENTRIES); \ - __uint(pinning, LIBBPF_PIN_BY_NAME); \ - __array(values, struct INNER_NAME); \ -} NAME SEC(".maps"); + struct { \ + __uint(type, TYPE); \ + KEY_TYPE *key; \ + VALUE_TYPE *value; \ + __uint(max_entries, MAX_ENTRIES); \ + __array(values, struct INNER_NAME); \ + } NAME SEC(".maps") = {.values = {&INNER_NAME}}; #define REGISTER_TABLE_NO_KEY_TYPE(NAME, TYPE, KEY_SIZE, VALUE_TYPE, MAX_ENTRIES) \ -struct { \ - __uint(type, TYPE); \ - __uint(key_size, KEY_SIZE); \ - VALUE_TYPE *value; \ - __uint(max_entries, MAX_ENTRIES); \ - __uint(pinning, LIBBPF_PIN_BY_NAME); \ -} NAME SEC(".maps"); -#endif + struct { \ + __uint(type, TYPE); \ + __uint(key_size, KEY_SIZE); \ + VALUE_TYPE *value; \ + __uint(max_entries, MAX_ENTRIES); \ + } NAME SEC(".maps"); #define REGISTER_END() -#define BPF_MAP_LOOKUP_ELEM(table, key) \ - bpf_map_lookup_elem(&table, key) -#define BPF_MAP_UPDATE_ELEM(table, key, value, flags) \ - bpf_map_update_elem(&table, key, value, flags) -#define BPF_MAP_DELETE_ELEM(table, key) \ - bpf_map_delete_elem(&table, key) -#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags)\ - bpf_update_elem(index, key, value, flags) +#define BPF_MAP_LOOKUP_ELEM(table, key) bpf_map_lookup_elem(&table, key) +#define BPF_MAP_UPDATE_ELEM(table, key, value, flags) bpf_map_update_elem(&table, key, value, flags) +#define BPF_MAP_DELETE_ELEM(table, key) bpf_map_delete_elem(&table, key) +#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags) bpf_update_elem(index, key, value, flags) #define BPF_OBJ_PIN(table, name) bpf_obj_pin(table, name) #define BPF_OBJ_GET(name) bpf_obj_get(name) -#endif // END EBPF KERNEL DEFINITIONS +#endif // END EBPF KERNEL DEFINITIONS #endif // BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_ diff --git a/backends/ebpf/runtime/kernel.mk b/backends/ebpf/runtime/kernel.mk index dd20570994..edfe554e72 100644 --- a/backends/ebpf/runtime/kernel.mk +++ b/backends/ebpf/runtime/kernel.mk @@ -3,7 +3,7 @@ ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) # Argument for the CLANG compiler LLC ?= llc CLANG ?= clang -override INCLUDES+= -I$(ROOT_DIR) -I$(ROOT_DIR)usr/include/ -I$(ROOT_DIR)contrib/libbpf/include/uapi/ +override INCLUDES+= -I$(ROOT_DIR) override LIBS+= # Optimization flags to save space override CFLAGS+= -O2 -g -c -D__KERNEL__ -D__ASM_SYSREG_H \ diff --git a/backends/ebpf/target.cpp b/backends/ebpf/target.cpp index c58926bc33..919ebbb6c8 100644 --- a/backends/ebpf/target.cpp +++ b/backends/ebpf/target.cpp @@ -98,7 +98,6 @@ void KernelSamplesTarget::emitTableDecl(Util::SourceCodeBuilder *builder, cstrin flags); } builder->newline(); - annotateTableWithBTF(builder, tblName, keyType, valueType); } void KernelSamplesTarget::emitTableDeclSpinlock(Util::SourceCodeBuilder *builder, cstring tblName, @@ -131,14 +130,12 @@ void KernelSamplesTarget::emitMapInMapDecl(Util::SourceCodeBuilder *builder, cst builder->appendFormat(registerInnerTable, innerName, kind, innerKeyType, innerValueType, innerSize, innerMapIndex, innerMapIndex); builder->newline(); - annotateTableWithBTF(builder, innerName, innerKeyType, innerValueType); kind = getBPFMapType(outerTableKind); cstring keyType = outerTableKind == TableArray ? "__u32"_cs : outerKeyType; builder->appendFormat(registerOuterTable, outerName, kind, keyType, "__u32", outerSize, innerMapIndex, innerName); builder->newline(); - annotateTableWithBTF(builder, outerName, keyType, "__u32"_cs); } void KernelSamplesTarget::emitLicense(Util::SourceCodeBuilder *builder, cstring license) const { @@ -202,12 +199,6 @@ void KernelSamplesTarget::emitTraceMessage(Util::SourceCodeBuilder *builder, con builder->newline(); } -void KernelSamplesTarget::annotateTableWithBTF(Util::SourceCodeBuilder *builder, cstring name, - cstring keyType, cstring valueType) const { - builder->appendFormat("BPF_ANNOTATE_KV_PAIR(%v, %v, %v)", name, keyType, valueType); - builder->newline(); -} - ////////////////////////////////////////////////////////////// void XdpTarget::emitResizeBuffer(Util::SourceCodeBuilder *builder, cstring buffer, cstring offsetVar) const { diff --git a/backends/ebpf/target.h b/backends/ebpf/target.h index 4a8bb81343..a74c7c1011 100644 --- a/backends/ebpf/target.h +++ b/backends/ebpf/target.h @@ -195,9 +195,6 @@ class KernelSamplesTarget : public Target { cstring sysMapPath() const override { return "/sys/fs/bpf/tc/globals"_cs; } cstring packetDescriptorType() const override { return "struct __sk_buff"_cs; } - - void annotateTableWithBTF(Util::SourceCodeBuilder *builder, cstring name, cstring keyType, - cstring valueType) const; }; class P4TCTarget : public KernelSamplesTarget { diff --git a/backends/ebpf/targets/ebpfenv.py b/backends/ebpf/targets/ebpfenv.py index 69b04f9026..21112e43e9 100644 --- a/backends/ebpf/targets/ebpfenv.py +++ b/backends/ebpf/targets/ebpfenv.py @@ -96,7 +96,7 @@ def ns_proc_append(self, proc: subprocess.Popen, cmd: str) -> int: def ns_proc_close(self, proc: subprocess.Popen, **extra_args: Any) -> int: """Close and actually run the process in the namespace. Returns the exit code.""" - testutils.log.info("Executing command: %s", proc) + testutils.log.info("Executing command: %s", proc.args) result = testutils.run_process(proc, timeout=testutils.TIMEOUT, **extra_args) if result.returncode != testutils.SUCCESS: testutils.log.error( @@ -109,7 +109,7 @@ def _configure_bridge(self, br_name: str) -> int: avoid ICMPv6 spam.""" # We do not care about failures here self.ns_exec(f"ip link set dev {br_name} up") - self.ns_exec(f"ip link set dev {br_name} mtu 9000") + self.ns_exec(f"ip link set dev {br_name} mtu 1500") # Prevent the broadcasting of ipv6 link discovery messages self.ns_exec("sysctl -w net.ipv6.conf.all.disable_ipv6=1") self.ns_exec("sysctl -w net.ipv6.conf.default.disable_ipv6=1") @@ -130,7 +130,7 @@ def _configure_bridge_port(self, port_name: str) -> int: result = self.ns_exec(cmd) if result != testutils.SUCCESS: return result - cmd = f"ip link set dev {port_name} mtu 9000" + cmd = f"ip link set dev {port_name} mtu 1500" return self.ns_exec(cmd) def attach_interfaces(self, num_ifaces: int) -> int: diff --git a/backends/ebpf/targets/kernel_target.py b/backends/ebpf/targets/kernel_target.py index 571c0447bd..b4353368d7 100644 --- a/backends/ebpf/targets/kernel_target.py +++ b/backends/ebpf/targets/kernel_target.py @@ -20,6 +20,7 @@ import time from glob import glob from pathlib import Path +from typing import Optional from .ebpfenv import Bridge from .target import EBPFTarget @@ -33,10 +34,15 @@ class Target(EBPFTarget): - EBPF_MAP_PATH = "/sys/fs/bpf/tc/globals" + EBPF_PATH = Path("/sys/fs/bpf") def __init__(self, tmpdir, options, template): EBPFTarget.__init__(self, tmpdir, options, template) + self.bpftool = self.runtimedir.joinpath("install/bpftool") + if self.options.target == "xdp": + self.ebpf_map_path = self.EBPF_PATH.joinpath(f"xdp/globals") + else: + self.ebpf_map_path = self.EBPF_PATH.joinpath(f"tc/globals") def compile_dataplane(self): # Use clang to compile the generated C code to a LLVM IR @@ -68,10 +74,7 @@ def _create_runtime(self): args += "CFLAGS+=-DCONTROL_PLANE " # add the folder local to the P4 file to the list of includes args += f"INCLUDES+=-I{os.path.dirname(self.options.p4filename)} " - # some kernel specific includes for libbpf - args += f"INCLUDES+=-I{self.runtimedir}/usr/include " - args += f"INCLUDES+=-I{self.runtimedir}/contrib/libbpf/include/uapi " - args += f"LIBS+={self.runtimedir}/usr/lib64/libbpf.a " + args += f"LIBS+={self.runtimedir}/install/libbpf/libbpf.a " args += "LIBS+=-lz " args += "LIBS+=-lelf " result = testutils.exec_process(args) @@ -79,19 +82,25 @@ def _create_runtime(self): testutils.log.error("Failed to build the filter") return result.returncode - def _create_bridge(self): + def _create_bridge(self) -> Optional[Bridge]: # The namespace is the id of the process namespace = str(os.getpid()) # Number of input files direction = "in" num_files = len(glob(self.filename("*", direction))) # Create the namespace and the bridge with all its ports - br = Bridge(namespace) - result = br.create_virtual_env(num_files) + bridge = Bridge(namespace) + result = bridge.create_virtual_env(num_files) if result != testutils.SUCCESS: - br.ns_del() + bridge.ns_del() return None - return br + if self.options.target != "xdp": + # Add the qdisc. MUST be clsact layer. + for port_name in bridge.edge_ports: + result = bridge.ns_exec(f"tc qdisc add dev {port_name} clsact") + if result != testutils.SUCCESS: + return None + return bridge def _get_run_cmd(self): direction = "in" @@ -108,42 +117,48 @@ def _get_run_cmd(self): cmd += "-d" return cmd - def _kill_processes(self, procs): + def _kill_processes(self, procs) -> None: for proc in procs: # kill process, 15 is SIGTERM os.kill(proc.pid, 15) - def _load_filter(self, bridge, proc, port_name): + def _attach_filters(self, bridge: Bridge, proc: subprocess.Popen) -> int: + # Is this a XDP or TC (ebpf_filter) program? + p_result = testutils.exec_process(f"objdump -hj xdp {self.template}.o").returncode + is_xdp = p_result == testutils.SUCCESS # Load the specified eBPF object to "port_name" egress # As a side-effect, this may create maps in /sys/fs/bpf/ + # Get the command to load eBPF code to all the attached ports + result = bridge.ns_proc_write(proc, f"mount -t bpf none {self.EBPF_PATH}") + if result != testutils.SUCCESS: + return result + result = bridge.ns_proc_append(proc, f"mkdir -p {self.ebpf_map_path}") + if result != testutils.SUCCESS: + return result + load_type = "xdp" if is_xdp else "tcx/egress" + cmd = f"{self.bpftool} prog load {self.template}.o {self.EBPF_PATH}/ebpf_filter pinmaps {self.ebpf_map_path} type {load_type}" + result = bridge.ns_proc_append(proc, cmd) + if result != testutils.SUCCESS: + return result - # Is this a XDP or TC (ebpf_filter) program? - result = testutils.exec_process(f"objdump -hj xdp {self.template}.o") - if result.returncode == testutils.SUCCESS: - # NB: XDP programs attach to the Rx end (but TC below attaches to Tx). - cmd = f"ip link set br_{port_name} xdpgeneric obj {self.template}.o sec xdp" + attach_type = "xdp" if is_xdp else "tcx_egress" + ports = bridge.br_ports if is_xdp else bridge.edge_ports + if len(ports) > 0: + for port_name in ports: + result = bridge.ns_proc_append( + proc, + f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {port_name}", + ) + if result != testutils.SUCCESS: + return result else: - # Add the qdisc. MUST be clsact layer. - bridge.ns_exec(f"tc qdisc add dev {port_name} clsact") - cmd = ( - f"tc filter add dev {port_name} egress" - f" bpf da obj {self.template}.o section prog verbose" + # No ports attached (no pcap files), load to bridge instead + result = bridge.ns_proc_append( + proc, + f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {bridge.br_name}", ) - return bridge.ns_proc_write(proc, cmd) - def _attach_filters(self, bridge, proc): - # Get the command to load eBPF code to all the attached ports - if len(bridge.edge_ports) > 0: - for port in bridge.edge_ports: - result = self._load_filter(bridge, proc, port) - bridge.ns_proc_append(proc, "") - else: - # No ports attached (no pcap files), load to bridge instead - result = self._load_filter(bridge, proc, bridge.br_name) - bridge.ns_proc_append(proc, "") - if result != testutils.SUCCESS: - return result - return testutils.SUCCESS + return result def _run_tcpdump(self, bridge, filename, port): cmd = f"{bridge.get_ns_prefix()} tcpdump -w {filename} -i {port}" @@ -166,10 +181,6 @@ def _run_in_namespace(self, bridge): return testutils.FAILURE dump_procs = self._init_tcpdump_listeners(bridge) result = self._attach_filters(bridge, proc) - if result != testutils.SUCCESS: - return result - # Check if eBPF maps have actually been created - result = bridge.ns_proc_write(proc, f"ls -1 {self.EBPF_MAP_PATH}") if result != testutils.SUCCESS: return result # Finally, append the actual runtime command to the process diff --git a/cmake/Linters.cmake b/cmake/Linters.cmake index ecab52b80e..616f1e77b5 100644 --- a/cmake/Linters.cmake +++ b/cmake/Linters.cmake @@ -136,7 +136,7 @@ file( tools/*.py ) list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/p4tools/submodules") -list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/ebpf/runtime/contrib/libbpf") +list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/ebpf/runtime/contrib") list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/tofino/third_party") list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "tools/cpplint.py") diff --git a/testdata/extern_modules/extern-conntrack-ebpf.c b/testdata/extern_modules/extern-conntrack-ebpf.c index 2b90d0a3b7..f5046fd6b8 100644 --- a/testdata/extern_modules/extern-conntrack-ebpf.c +++ b/testdata/extern_modules/extern-conntrack-ebpf.c @@ -29,7 +29,7 @@ struct connInfo { }; REGISTER_START() -REGISTER_TABLE(tcp_reg, BPF_MAP_TYPE_HASH, sizeof(u32), sizeof(struct connInfo), MAX_ENTRIES) +REGISTER_TABLE(tcp_reg, BPF_MAP_TYPE_HASH, u32, struct connInfo, MAX_ENTRIES) REGISTER_END() static inline u8 tcp_conntrack(struct Headers_t hdrs)