Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds BPF program iterator (C++) #2000

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.15)
project(collector)

LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

enable_testing()

add_subdirectory(collector)
190 changes: 190 additions & 0 deletions cmake/FindBpfObject.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause

#[=======================================================================[.rst:
FindBpfObject
--------

Find BpfObject

This module finds if all the dependencies for eBPF Compile-Once-Run-Everywhere
programs are available and where all the components are located.

The caller may set the following variables to disable automatic
search/processing for the associated component:

``BPFOBJECT_BPFTOOL_EXE``
Path to ``bpftool`` binary

``BPFOBJECT_CLANG_EXE``
Path to ``clang`` binary

``LIBBPF_INCLUDE_DIRS``
Path to ``libbpf`` development headers

``LIBBPF_LIBRARIES``
Path to `libbpf` library

``BPFOBJECT_VMLINUX_H``
Path to ``vmlinux.h`` generated by ``bpftool``. If unset, this module will
attempt to automatically generate a copy.

This module sets the following result variables:

::

BpfObject_FOUND = TRUE if all components are found


This module also provides the ``bpf_object()`` macro. This macro generates a
cmake interface library for the BPF object's generated skeleton as well
as the associated dependencies.

.. code-block:: cmake

bpf_object(<name> <source> [<header> ...])

Given an abstract ``<name>`` for a BPF object and the associated ``<source>``
file, generates an interface library target, ``<name>_skel``, that may be
linked against by other cmake targets. Additional headers may be provided to
the macro to ensure that the generated skeleton is up-to-date.

Example Usage:

::

find_package(BpfObject REQUIRED)
bpf_object(myobject myobject.bpf.c myobject.h)
add_executable(myapp myapp.c)
target_link_libraries(myapp myobject_skel)

#]=======================================================================]

if(NOT BPFOBJECT_BPFTOOL_EXE)
find_program(BPFOBJECT_BPFTOOL_EXE NAMES bpftool DOC "Path to bpftool executable")
endif()

if(NOT BPFOBJECT_CLANG_EXE)
find_program(BPFOBJECT_CLANG_EXE NAMES clang DOC "Path to clang executable")

execute_process(COMMAND ${BPFOBJECT_CLANG_EXE} --version
OUTPUT_VARIABLE CLANG_version_output
ERROR_VARIABLE CLANG_version_error
RESULT_VARIABLE CLANG_version_result
OUTPUT_STRIP_TRAILING_WHITESPACE)

# Check that clang is new enough
if(${CLANG_version_result} EQUAL 0)
if("${CLANG_version_output}" MATCHES "clang version ([^\n]+)\n")
# Transform X.Y.Z into X;Y;Z which can then be interpreted as a list
set(CLANG_VERSION "${CMAKE_MATCH_1}")
string(REPLACE "." ";" CLANG_VERSION_LIST ${CLANG_VERSION})
list(GET CLANG_VERSION_LIST 0 CLANG_VERSION_MAJOR)

# Anything older than clang 10 doesn't really work
string(COMPARE LESS ${CLANG_VERSION_MAJOR} 10 CLANG_VERSION_MAJOR_LT10)
if(${CLANG_VERSION_MAJOR_LT10})
message(FATAL_ERROR "clang ${CLANG_VERSION} is too old for BPF CO-RE")
endif()

message(STATUS "Found clang version: ${CLANG_VERSION}")
else()
message(FATAL_ERROR "Failed to parse clang version string: ${CLANG_version_output}")
endif()
else()
message(FATAL_ERROR "Command \"${BPFOBJECT_CLANG_EXE} --version\" failed with output:\n${CLANG_version_error}")
endif()
endif()

if(NOT LIBBPF_INCLUDE_DIRS OR NOT LIBBPF_LIBRARIES)
find_package(LibBpf)
endif()

if(BPFOBJECT_VMLINUX_H)
get_filename_component(GENERATED_VMLINUX_DIR ${BPFOBJECT_VMLINUX_H} DIRECTORY)
elseif(BPFOBJECT_BPFTOOL_EXE)
# Generate vmlinux.h
set(GENERATED_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(BPFOBJECT_VMLINUX_H ${GENERATED_VMLINUX_DIR}/vmlinux.h)
execute_process(COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c
OUTPUT_FILE ${BPFOBJECT_VMLINUX_H}
ERROR_VARIABLE VMLINUX_error
RESULT_VARIABLE VMLINUX_result)
if(${VMLINUX_result} EQUAL 0)
set(VMLINUX ${BPFOBJECT_VMLINUX_H})
else()
message(FATAL_ERROR "Failed to dump vmlinux.h from BTF: ${VMLINUX_error}")
endif()
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(BpfObject
REQUIRED_VARS
BPFOBJECT_BPFTOOL_EXE
BPFOBJECT_CLANG_EXE
LIBBPF_INCLUDE_DIRS
LIBBPF_LIBRARIES
GENERATED_VMLINUX_DIR)

# Get clang bpf system includes
execute_process(
COMMAND bash -c "${BPFOBJECT_CLANG_EXE} -v -E - < /dev/null 2>&1 |
sed -n '/<...> search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }'"
OUTPUT_VARIABLE CLANG_SYSTEM_INCLUDES_output
ERROR_VARIABLE CLANG_SYSTEM_INCLUDES_error
RESULT_VARIABLE CLANG_SYSTEM_INCLUDES_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${CLANG_SYSTEM_INCLUDES_result} EQUAL 0)
separate_arguments(CLANG_SYSTEM_INCLUDES UNIX_COMMAND ${CLANG_SYSTEM_INCLUDES_output})
message(STATUS "BPF system include flags: ${CLANG_SYSTEM_INCLUDES}")
else()
message(FATAL_ERROR "Failed to determine BPF system includes: ${CLANG_SYSTEM_INCLUDES_error}")
endif()

# Get target arch
execute_process(COMMAND uname -m
COMMAND sed -e "s/x86_64/x86/" -e "s/aarch64/arm64/" -e "s/ppc64le/powerpc/" -e "s/mips.*/mips/" -e "s/riscv64/riscv/"
OUTPUT_VARIABLE ARCH_output
ERROR_VARIABLE ARCH_error
RESULT_VARIABLE ARCH_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${ARCH_result} EQUAL 0)
set(ARCH ${ARCH_output})
message(STATUS "BPF target arch: ${ARCH}")
else()
message(FATAL_ERROR "Failed to determine target architecture: ${ARCH_error}")
endif()

# Public macro
macro(bpf_object name input)
set(BPF_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${input})
foreach(arg ${ARGN})
list(APPEND BPF_H_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
endforeach()
set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.bpf.o)
set(BPF_SKEL_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.skel.h)
set(OUTPUT_TARGET ${name}_skel)

# Build BPF object file
add_custom_command(OUTPUT ${BPF_O_FILE}
COMMAND ${BPFOBJECT_CLANG_EXE} -g -O2 -target bpf -D__TARGET_ARCH_${ARCH}
${CLANG_SYSTEM_INCLUDES} -I${GENERATED_VMLINUX_DIR}
-isystem ${LIBBPF_INCLUDE_DIRS} -c ${BPF_C_FILE} -o ${BPF_O_FILE}
COMMAND_EXPAND_LISTS
VERBATIM
DEPENDS ${BPF_C_FILE} ${BPF_H_FILES}
COMMENT "[clang] Building BPF object: ${name}")

# Build BPF skeleton header
add_custom_command(OUTPUT ${BPF_SKEL_FILE}
COMMAND bash -c "${BPFOBJECT_BPFTOOL_EXE} gen skeleton ${BPF_O_FILE} > ${BPF_SKEL_FILE}"
VERBATIM
DEPENDS ${BPF_O_FILE}
COMMENT "[skel] Building BPF skeleton: ${name}")

add_library(${OUTPUT_TARGET} INTERFACE)
target_sources(${OUTPUT_TARGET} INTERFACE ${BPF_SKEL_FILE})
target_include_directories(${OUTPUT_TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(${OUTPUT_TARGET} SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS})
target_link_libraries(${OUTPUT_TARGET} INTERFACE ${LIBBPF_LIBRARIES} -lelf -lz)
endmacro()

32 changes: 32 additions & 0 deletions cmake/FindLibBpf.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause

find_path(LIBBPF_INCLUDE_DIRS
NAMES
bpf/bpf.h
bpf/btf.h
bpf/libbpf.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
ENV CPATH)

find_library(LIBBPF_LIBRARIES
NAMES
bpf
PATHS
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
ENV LIBRARY_PATH
ENV LD_LIBRARY_PATH)

include (FindPackageHandleStandardArgs)

FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf "Please install the libbpf development package"
LIBBPF_LIBRARIES
LIBBPF_INCLUDE_DIRS)

mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES)
2 changes: 2 additions & 0 deletions collector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,5 @@ set(MODERN_BPF_DEBUG_MODE ${BPF_DEBUG_MODE} CACHE BOOL "Enable BPF debug prints"
set(MODERN_BPF_EXCLUDE_PROGS "^(openat2|ppoll|setsockopt|io_uring_setup|nanosleep)$" CACHE STRING "Set of syscalls to exclude from modern bpf engine " FORCE)

add_subdirectory(${FALCO_DIR} falco)

add_subdirectory(standalone)
7 changes: 6 additions & 1 deletion collector/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
find_package(BpfObject REQUIRED)

add_subdirectory(sources)

file(GLOB COLLECTOR_LIB_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/system-inspector/*.cpp
)

add_library(collector_lib ${DRIVER_HEADERS} ${COLLECTOR_LIB_SRC_FILES})
add_dependencies(collector_lib sinsp)
add_dependencies(collector_lib sinsp collector_scrapers)
target_link_libraries(collector_lib sinsp)
target_link_libraries(collector_lib stdc++fs) # This is needed for GCC-8 to link against the filesystem library
target_link_libraries(collector_lib cap-ng)
Expand All @@ -14,6 +18,7 @@ target_link_libraries(collector_lib civetweb::civetweb-cpp)
target_link_libraries(collector_lib yaml-cpp::yaml-cpp)

target_link_libraries(collector_lib rox-proto)
target_link_libraries(collector_lib collector_scrapers)

if(NOT DISABLE_PROFILING)
find_library(GPERFTOOLS_PROFILER profiler)
Expand Down
2 changes: 2 additions & 0 deletions collector/lib/CollectorService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ void CollectorService::RunForever() {
server.addHandler(collector_config_inspector->kBaseRoute, collector_config_inspector.get());
}

bpf_programs_.Load();

system_inspector_.Init(config_, conn_tracker);
system_inspector_.Start();

Expand Down
2 changes: 2 additions & 0 deletions collector/lib/CollectorService.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "CollectorConfig.h"
#include "Control.h"
#include "sources/bpf-scraper/BPFProgramIterator.h"
#include "system-inspector/Service.h"

namespace collector {
Expand All @@ -24,6 +25,7 @@ class CollectorService {
const std::atomic<int>& signum_;

system_inspector::Service system_inspector_;
sources::BPFProgramIterator bpf_programs_;
};

bool SetupKernelDriver(CollectorService& collector, const CollectorConfig& config);
Expand Down
2 changes: 2 additions & 0 deletions collector/lib/sources/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

add_subdirectory(bpf-scraper)
65 changes: 65 additions & 0 deletions collector/lib/sources/bpf-scraper/BPFProgramIterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "BPFProgramIterator.h"

#include <unistd.h>

#include <bpf/bpf.h>
#include <bpf/libbpf.h>

#include "bpf/common.h"
#include "program_iter.skel.h"

namespace collector::sources {

static struct program_iter_bpf* skeleton_;

bool BPFProgramIterator::Load() {
skeleton_ = program_iter_bpf__open_and_load();
if (!skeleton_) {
return false;
}

if (program_iter_bpf__attach(skeleton_) != 0) {
return false;
}

return true;
}

void BPFProgramIterator::Unload() {
program_iter_bpf__destroy(skeleton_);
}

std::vector<sensor::SignalStreamMessage*> BPFProgramIterator::LoadedPrograms() {
int iter_fd = bpf_iter_create(bpf_link__fd(skeleton_->links.dump_bpf_prog));
if (iter_fd < 0) {
return {};
}

std::vector<sensor::SignalStreamMessage*> messages;
struct bpf_prog_result result;

while (true) {
int ret = read(iter_fd, &result, sizeof(struct bpf_prog_result));
if (ret < 0) {
if (errno == EAGAIN) {
continue;
}
break;
}

if (ret == 0) {
break;
}

auto message = formatter_.ToProtoMessage(&result);
if (message == nullptr) {
continue;
}

messages.push_back(message);
}

return messages;
}

} // namespace collector::sources
23 changes: 23 additions & 0 deletions collector/lib/sources/bpf-scraper/BPFProgramIterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef _COLLECTOR_BPF_PROGRAM_ITERATOR_
#define _COLLECTOR_BPF_PROGRAM_ITERATOR_

#include <vector>

#include "internalapi/sensor/signal_iservice.pb.h"

#include "BPFSignalFormatter.h"

namespace collector::sources {
class BPFProgramIterator {
public:
bool Load();
void Unload();

std::vector<sensor::SignalStreamMessage*> LoadedPrograms();

private:
BPFSignalFormatter formatter_;
};
}; // namespace collector::sources

#endif
Loading
Loading