Skip to content

Commit

Permalink
Use bpftool instead of iproute2 to load eBPF and XDP maps.
Browse files Browse the repository at this point in the history
Signed-off-by: fruffy <[email protected]>
  • Loading branch information
fruffy committed Dec 23, 2024
1 parent d816fd6 commit 0b1d7a4
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 204 deletions.
8 changes: 4 additions & 4 deletions backends/ebpf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ 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
)
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(
Expand Down
27 changes: 15 additions & 12 deletions backends/ebpf/build_libbpf
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand All @@ -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


Expand Down
4 changes: 2 additions & 2 deletions backends/ebpf/run-ebpf-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.


Expand Down
1 change: 1 addition & 0 deletions backends/ebpf/runtime/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
install
1 change: 1 addition & 0 deletions backends/ebpf/runtime/contrib/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
libbpf
bpftool
178 changes: 50 additions & 128 deletions backends/ebpf/runtime/ebpf_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ 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.
#ifndef BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_
#define BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_

#include "ebpf_common.h"

#include <bpf/bpf_endian.h> // definitions for bpf_ntohs etc...
#include "install/libbpf/include/bpf/bpf_endian.h" // definitions for bpf_ntohs etc...

#undef htonl
#undef htons
Expand All @@ -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/bpf.h> // 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 <linux/pkt_cls.h> // 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 <bpf/bpf_helpers.h>

/// 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_
2 changes: 1 addition & 1 deletion backends/ebpf/runtime/kernel.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
9 changes: 0 additions & 9 deletions backends/ebpf/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 0 additions & 3 deletions backends/ebpf/target.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions backends/ebpf/targets/ebpfenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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")
Expand All @@ -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:
Expand Down
Loading

0 comments on commit 0b1d7a4

Please sign in to comment.