From 1a6f4e2bf3f1b7bebe21372fe4ded1e15da46400 Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 09:07:04 +0200 Subject: [PATCH 01/16] feat: Implement logging for userspace --- CMakeLists.txt | 6 +- ebpfdiscoverysrv/CMakeLists.txt | 1 + ebpfdiscoverysrv/src/main.cpp | 184 +++++++++++++++--- libebpfdiscovery/CMakeLists.txt | 7 +- .../headers/ebpfdiscovery/Discovery.h | 47 +++-- .../headers/ebpfdiscovery/DiscoveryBpf.h | 20 ++ .../ebpfdiscovery/DiscoveryBpfLoader.h | 32 +++ libebpfdiscovery/src/Discovery.cpp | 154 +++++++-------- libebpfdiscovery/src/DiscoveryBpf.cpp | 9 + libebpfdiscovery/src/DiscoveryBpfLoader.cpp | 60 ++++++ libebpfdiscoveryskel/src/Config.h | 14 ++ liblogging/CMakeLists.txt | 6 + liblogging/headers/logging/Global.h | 23 +++ liblogging/headers/logging/Logger.h | 118 +++++++++++ liblogging/src/Global.cpp | 11 ++ liblogging/src/Logger.cpp | 120 ++++++++++++ 16 files changed, 677 insertions(+), 135 deletions(-) create mode 100644 libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpf.h create mode 100644 libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpfLoader.h create mode 100644 libebpfdiscovery/src/DiscoveryBpf.cpp create mode 100644 libebpfdiscovery/src/DiscoveryBpfLoader.cpp create mode 100644 libebpfdiscoveryskel/src/Config.h create mode 100644 liblogging/CMakeLists.txt create mode 100644 liblogging/headers/logging/Global.h create mode 100644 liblogging/headers/logging/Logger.h create mode 100644 liblogging/src/Global.cpp create mode 100644 liblogging/src/Logger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ffeabfee..b7c36322 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,9 @@ endif() if(NOT TARGET fmt) find_package(fmt REQUIRED) endif(NOT TARGET fmt) +if(NOT TARGET spdlog) + find_package(spdlog REQUIRED) +endif(NOT TARGET spdlog) find_package(Protobuf REQUIRED) @@ -102,11 +105,12 @@ set(LIBBPF_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/thirdparty/libbpf) set(LIBBPF_LIBRARIES ${CMAKE_BINARY_DIR}/thirdparty/libbpf/libbpf.a) add_subdirectory(libbpfload) -add_subdirectory(libebpfdiscoveryproto) add_subdirectory(libebpfdiscovery) +add_subdirectory(libebpfdiscoveryproto) add_subdirectory(libebpfdiscoveryshared) add_subdirectory(libebpfdiscoveryskel) add_subdirectory(libhttpparser) +add_subdirectory(liblogging) include(GNUInstallDirs) add_subdirectory(ebpfdiscoverysrv) diff --git a/ebpfdiscoverysrv/CMakeLists.txt b/ebpfdiscoverysrv/CMakeLists.txt index 1c4a5b9b..384c8d3f 100644 --- a/ebpfdiscoverysrv/CMakeLists.txt +++ b/ebpfdiscoverysrv/CMakeLists.txt @@ -5,3 +5,4 @@ add_executable(${TARGET} ${SOURCES}) target_link_libraries(${TARGET} Boost::program_options) target_link_libraries(${TARGET} ebpfdiscovery) +target_link_libraries(${TARGET} logging) diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index 473ef25f..8153dbae 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -1,62 +1,192 @@ // SPDX-License-Identifier: GPL-2.0 - #include "ebpfdiscovery/Discovery.h" +#include "ebpfdiscovery/DiscoveryBpf.h" +#include "ebpfdiscovery/DiscoveryBpfLoader.h" +#include "logging/Global.h" -#include +#include +#include +#include #include #include #include +#include #include -static ebpfdiscovery::Discovery discoveryInstance; +enum class ProgramStatus { + Running, + UnixShutdownSignalReceived, +} programStatus; -std::atomic isShuttingDown; +std::condition_variable programStatusCV; +std::mutex programStatusMutex; -static void handleUnixExitSignal(int signo) { - if (isShuttingDown) { - return; - } - isShuttingDown = true; +/* + * CLI options + */ + +using logging::LogLevel; + +static boost::program_options::options_description getProgramOptions() { + namespace po = boost::program_options; + po::options_description desc{"Options"}; + + // clang-format off + desc.add_options() + ("log-level", po::value()->default_value(logging::LogLevel::Err, "error"), "Set log level {trace,debug,info,warning,error,critical,off}") + ("help,h", "Display available options") + ("log-dir", po::value()->default_value(""), "Log files directory") + ("log-no-stdout", po::value()->default_value(false), "Disable logging to stdout") + ("version", "Display program version") + ; + + // clang-format on + return desc; +} + +static std::string getProgramVersion() { + std::ostringstream ss; + ss << EBPFDISCOVERY_VERSION_MAJOR << "." << EBPFDISCOVERY_VERSION_MINOR << "." << EBPFDISCOVERY_VERSION_PATCH; + return ss.str(); +} + +/* + * Logging setup + */ - discoveryInstance.stopRun(); +static void setupLogging(logging::LogLevel logLevel, bool enableStdout, std::string_view logDir) { + logging::Global::getInstance().setup("eBPF-Discovery", enableStdout, logDir); + logging::Global::getInstance().setLevel(logLevel); + LOG_TRACE("Logging has been set up. (logDir: {})", logDir); } -void setupUnixSignalHandlers() { - struct sigaction action {}; - action.sa_handler = handleUnixExitSignal; - action.sa_flags = 0; - sigaction(SIGINT, &action, nullptr); - sigaction(SIGTERM, &action, nullptr); - sigaction(SIGPIPE, &action, nullptr); +/* + * Unix signals setup + */ + +static sigset_t getSigset() { + sigset_t sigset; + sigfillset(&sigset); + return sigset; } +static void runUnixSignalHandlerLoop() { + while (true) { + sigset_t sigset{getSigset()}; + LOG_TRACE("Waiting for unix signals."); + int signo{sigwaitinfo(&sigset, nullptr)}; + if (signo == -1) { + LOG_CRITICAL("Failed to wait for unix signals: {}", std::strerror(errno)); + std::abort(); + } + LOG_DEBUG("Received unix signal. (signo: {})", signo); + if (signo == SIGINT || signo == SIGPIPE || signo == SIGTERM) { + std::lock_guard lock(programStatusMutex); + programStatus = ProgramStatus::UnixShutdownSignalReceived; + LOG_TRACE("Unix signal handler is notifying for shutdown."); + programStatusCV.notify_all(); + break; + } + } +} + +/* + * Libbpf setup + */ + static int libbpfPrintFn(enum libbpf_print_level level, const char* format, va_list args) { -#ifdef DEBUG - return vfprintf(stderr, format, args); -#else + switch (level) { + case LIBBPF_WARN: + logging::Global::getInstance().vlogf(logging::LogLevel::Warn, format, args); + return 0; + case LIBBPF_INFO: + logging::Global::getInstance().vlogf(logging::LogLevel::Info, format, args); + return 0; + case LIBBPF_DEBUG: + logging::Global::getInstance().vlogf(logging::LogLevel::Debug, format, args); + return 0; + } return 0; -#endif } -void setupLibbpf() { +static void setupLibbpf() { libbpf_set_print(libbpfPrintFn); } int main(int argc, char** argv) { - setupLibbpf(); - setupUnixSignalHandlers(); + namespace po = boost::program_options; + po::options_description desc{getProgramOptions()}; + po::variables_map vm; + + try { + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + } catch (const po::error& e) { + std::cout << e.what() << '\n'; + return EXIT_FAILURE; + } + + if (vm.count("help")) { + std::cout << desc; + return EXIT_SUCCESS; + } + + if (vm.count("version")) { + std::cout << "eBPF-Discovery " << getProgramVersion() << '\n'; + return EXIT_SUCCESS; + } + + logging::LogLevel logLevel{vm["log-level"].as()}; + bool isStdoutLogDisabled{vm["log-no-stdout"].as()}; + std::string logDir{vm["log-dir"].as()}; try { - discoveryInstance.load(); + setupLogging(logLevel, !isStdoutLogDisabled, logDir); } catch (const std::runtime_error& e) { - std::cerr << "Couldn't load BPF program: " << e.what() << std::endl; + std::cerr << "Couldn't setup logging: " << e.what() << '\n'; return EXIT_FAILURE; } - if (discoveryInstance.run() != 0) { + LOG_DEBUG("Starting the program."); + + { + LOG_TRACE("Setting up unix signals handling."); + sigset_t sigset = getSigset(); + if (sigprocmask(SIG_BLOCK, &sigset, nullptr) == -1) { + LOG_CRITICAL("Failed to block unix signals: {}", std::strerror(errno)); + return EXIT_FAILURE; + } + } + + setupLibbpf(); + ebpfdiscovery::DiscoveryBpfLoader loader; + try { + loader.load(); + } catch (const std::runtime_error& e) { + LOG_CRITICAL("Couldn't load BPF program. ({})", e.what()); return EXIT_FAILURE; } + ebpfdiscovery::Discovery instance(loader.get()); + try { + instance.start(); + } catch (const std::runtime_error& e) { + LOG_CRITICAL("Couldn't start Discovery: {}", e.what()); + } + + std::thread unixSignalThread(runUnixSignalHandlerLoop); + { + std::unique_lock programStatusLock(programStatusMutex); + programStatusCV.wait(programStatusLock, []() { return programStatus != ProgramStatus::Running; }); + } + + LOG_DEBUG("Exiting the program."); + if (unixSignalThread.joinable()) { + unixSignalThread.join(); + } + instance.stop(); + instance.wait(); + return EXIT_SUCCESS; } diff --git a/libebpfdiscovery/CMakeLists.txt b/libebpfdiscovery/CMakeLists.txt index 6888c764..65c2f31f 100644 --- a/libebpfdiscovery/CMakeLists.txt +++ b/libebpfdiscovery/CMakeLists.txt @@ -3,6 +3,9 @@ list( SOURCES src/Config.cpp src/Discovery.cpp + src/DiscoveryBpf.cpp + src/DiscoveryBpfLoader.cpp + # src/DiscoveryBpfLogHandler.cpp src/Session.cpp src/StringFunctions.cpp ) @@ -12,11 +15,13 @@ add_library(${TARGET} STATIC ${SOURCES}) target_include_directories(${TARGET} PRIVATE src PUBLIC headers) target_link_libraries(${TARGET} bpfload) +target_link_libraries(${TARGET} ebpfdiscoveryproto) target_link_libraries(${TARGET} ebpfdiscoveryshared) target_link_libraries(${TARGET} ebpfdiscoveryskel) target_link_libraries(${TARGET} fmt::fmt) target_link_libraries(${TARGET} httpparser) -target_link_libraries(${TARGET} ebpfdiscoveryproto) +target_link_libraries(${TARGET} logging) +target_link_libraries(${TARGET} spdlog::spdlog) if(BUILD_TESTS) list(APPEND TEST_SOURCES test/StringFunctionsTest.cpp test/LRUCacheTest.cpp) diff --git a/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h b/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h index 427c6f16..736a44bf 100644 --- a/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h +++ b/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h @@ -2,17 +2,19 @@ #pragma once #include "ebpfdiscovery/Config.h" +#include "ebpfdiscovery/DiscoveryBpf.h" #include "ebpfdiscovery/LRUCache.h" #include "ebpfdiscovery/Session.h" #include "ebpfdiscoveryshared/Types.h" #include "httpparser/HttpRequestParser.h" -#include "discovery.skel.h" - #include #include +#include +#include #include #include +#include #include namespace ebpfdiscovery { @@ -21,23 +23,23 @@ using httpparser::HttpRequestParser; class Discovery { public: - Discovery(); - Discovery(const DiscoveryConfig config); - ~Discovery(); - - bool isLoaded() noexcept; - void load(); - void unload() noexcept; - - // Blocks current thread until stopRun() is called - int run(); - - // Thread safe operation - void stopRun(); + Discovery(DiscoveryBpf discoveryBpf); + Discovery(DiscoveryBpf discoveryBpf, const DiscoveryConfig config); + Discovery(const Discovery&) = delete; + Discovery& operator=(const Discovery&) = delete; + Discovery(Discovery&&) = default; + Discovery& operator=(Discovery&&) = default; + ~Discovery() = default; + + void start(); + void stop(); + void wait(); private: typedef LRUCache SavedSessionsCacheType; + void run(); + void fetchEvents(); void saveSession(const DiscoverySavedSessionKey& session_key, const Session& session); @@ -47,6 +49,7 @@ class Discovery { void handleBufferLookupSuccess(DiscoverySavedBuffer& savedBuffer, DiscoveryEvent& event); void handleExistingSession(SavedSessionsCacheType::iterator it, std::string_view& bufferView, DiscoveryEvent& event); void handleNewSession(std::string_view& bufferView, DiscoveryEvent& event); + void handleNewRequest(const Session& session, const DiscoverySessionMeta& meta); void handleCloseEvent(DiscoveryEvent& event); void handleSuccessfulParse(const Session& session, const DiscoverySessionMeta& sessionMeta); @@ -54,13 +57,19 @@ class Discovery { int bpfDiscoveryResumeCollecting(); int bpfDiscoveryDeleteSession(const DiscoveryTrackedSessionKey& trackedSessionKey); + constexpr auto discoverySkel() const { + return discoveryBpf.skel; + } + DiscoveryConfig config; + DiscoveryBpf discoveryBpf; + SavedSessionsCacheType savedSessions; std::atomic running; - std::atomic loaded; - discovery_bpf* discoverySkel; - bpf_object_open_opts discoverySkelOpenOpts; - SavedSessionsCacheType savedSessions; + bool stopReceived{false}; + std::condition_variable stopReceivedCV; + std::mutex stopReceivedMutex; + std::thread workerThread; }; } // namespace ebpfdiscovery diff --git a/libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpf.h b/libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpf.h new file mode 100644 index 00000000..1ca27af1 --- /dev/null +++ b/libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpf.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "discovery.skel.h" + +namespace ebpfdiscovery { + +class DiscoveryBpf { +public: + DiscoveryBpf(discovery_bpf* skel); + DiscoveryBpf(const DiscoveryBpf&) = default; + DiscoveryBpf& operator=(const DiscoveryBpf&) = default; + DiscoveryBpf(DiscoveryBpf&&) = default; + DiscoveryBpf& operator=(DiscoveryBpf&&) = default; + ~DiscoveryBpf() = default; + + discovery_bpf* skel; +}; + +} // namespace ebpfdiscovery diff --git a/libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpfLoader.h b/libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpfLoader.h new file mode 100644 index 00000000..b5d38aa8 --- /dev/null +++ b/libebpfdiscovery/headers/ebpfdiscovery/DiscoveryBpfLoader.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "ebpfdiscovery/DiscoveryBpf.h" + +#include "discovery.skel.h" + +#include + +namespace ebpfdiscovery { +class DiscoveryBpfLoader { +public: + DiscoveryBpfLoader(); + DiscoveryBpfLoader(const DiscoveryBpfLoader&) = delete; + DiscoveryBpfLoader& operator=(const DiscoveryBpfLoader&) = delete; + DiscoveryBpfLoader(DiscoveryBpfLoader&&) = default; + DiscoveryBpfLoader& operator=(DiscoveryBpfLoader&&) = default; + ~DiscoveryBpfLoader(); + + void load(); + void unload() noexcept; + bool isLoaded() noexcept; + + DiscoveryBpf get(); + +private: + std::atomic loaded; + bpf_object_open_opts openOpts; + discovery_bpf* skel; +}; + +} // namespace ebpfdiscovery diff --git a/libebpfdiscovery/src/Discovery.cpp b/libebpfdiscovery/src/Discovery.cpp index b0af007c..23227e38 100644 --- a/libebpfdiscovery/src/Discovery.cpp +++ b/libebpfdiscovery/src/Discovery.cpp @@ -1,15 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 - #include "ebpfdiscovery/Discovery.h" #include "StringFunctions.h" #include "ebpfdiscovery/Session.h" - -extern "C" { -#include "bpfload/btf_helpers.h" -} - -#include "discovery.skel.h" +#include "logging/Global.h" #include #include @@ -24,51 +18,54 @@ extern "C" { namespace ebpfdiscovery { -static void printSession(const Session& session, const DiscoverySessionMeta& meta) { - const auto& request{session.parser.result}; - std::cout << request.method << " " << request.host << request.url; - - if (const auto& xForwardedFor{request.xForwardedFor}; !xForwardedFor.empty()) { - std::cout << " X-Forwarded-For: " << '"' << xForwardedFor << '"'; - } else if (discoverySessionFlagsIsIPv4(meta.flags)) { - if (auto srcIpv4{ipv4ToString(meta.sourceIPData)}; !srcIpv4.empty()) - std::cout << " srcIpv4: " << '"' << srcIpv4 << '"'; - } else if (discoverySessionFlagsIsIPv6(meta.flags)) { - if (auto srcIpv6{ipv6ToString(meta.sourceIPData)}; !srcIpv6.empty()) - std::cout << " srcIpv6: " << '"' << srcIpv6 << '"'; - } - - std::cout << '\n'; +Discovery::Discovery(DiscoveryBpf discoveryBpf) : Discovery(discoveryBpf, DiscoveryConfig{}) { } -Discovery::Discovery() : Discovery(DiscoveryConfig{}) { +Discovery::Discovery(DiscoveryBpf discoveryBpf, const DiscoveryConfig config) + : discoveryBpf(discoveryBpf), savedSessions(DISCOVERY_MAX_SESSIONS) { } -Discovery::Discovery(const DiscoveryConfig config) : savedSessions(DISCOVERY_MAX_SESSIONS) { -} - -Discovery::~Discovery() { - unload(); -} +void Discovery::start() { + if (running) { + return; + } + running = true; -int Discovery::run() { - if (!isLoaded()) { - return -1; + if (int ret{bpfDiscoveryResumeCollecting()}; ret != 0) { + running = false; + throw std::runtime_error("Could not initialize BPF program configuration: " + std::to_string(ret)); } - running = true; - while (running) { + workerThread = std::thread([&]() { run(); }); +} + +void Discovery::run() { + LOG_TRACE("Discovery is starting the BPF event handler loop."); + std::unique_lock lock(stopReceivedMutex); + while (!stopReceived) { fetchEvents(); bpfDiscoveryResumeCollecting(); - std::this_thread::sleep_for(config.eventQueuePollInterval); + stopReceivedCV.wait_for(lock, config.eventQueuePollInterval); } - return 0; + return; +} + +void Discovery::stop() { + std::lock_guard lock(stopReceivedMutex); + stopReceived = true; + stopReceivedCV.notify_all(); +} + +void Discovery::wait() { + if (workerThread.joinable()) { + workerThread.join(); + } } void Discovery::fetchEvents() { DiscoveryEvent event; - while (bpf_map__lookup_and_delete_elem(discoverySkel->maps.eventsToUserspaceQueueMap, NULL, 0, &event, sizeof(event), BPF_ANY) == 0) { + while (bpf_map__lookup_and_delete_elem(discoverySkel()->maps.eventsToUserspaceQueueMap, NULL, 0, &event, sizeof(event), BPF_ANY) == 0) { handleNewEvent(std::move(event)); } } @@ -85,7 +82,7 @@ void Discovery::handleNewEvent(DiscoveryEvent event) { void Discovery::handleNewDataEvent(DiscoveryEvent& event) { DiscoverySavedBuffer savedBuffer; auto lookup_result{bpf_map__lookup_elem( - discoverySkel->maps.savedBuffersMap, + discoverySkel()->maps.savedBuffersMap, &event.dataKey, sizeof(DiscoverySavedBufferKey), &savedBuffer, @@ -100,7 +97,7 @@ void Discovery::handleNewDataEvent(DiscoveryEvent& event) { void Discovery::handleBufferLookupSuccess(DiscoverySavedBuffer& savedBuffer, DiscoveryEvent& event) { std::string_view bufferView(savedBuffer.data, savedBuffer.length); - bpf_map__delete_elem(discoverySkel->maps.savedBuffersMap, &event.dataKey, sizeof(DiscoverySavedBufferKey), BPF_ANY); + bpf_map__delete_elem(discoverySkel()->maps.savedBuffersMap, &event.dataKey, sizeof(DiscoverySavedBufferKey), BPF_ANY); auto it{savedSessions.find(event.dataKey)}; if (it != savedSessions.end()) { @@ -148,6 +145,34 @@ void Discovery::handleNewSession(std::string_view& bufferView, DiscoveryEvent& e handleSuccessfulParse(session, event.sessionMeta); } +void Discovery::handleNewRequest(const Session& session, const DiscoverySessionMeta& meta) { + const auto& request{session.parser.result}; + if (discoverySessionFlagsIsIPv4(meta.flags)) { + LOG_DEBUG( + "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', sourceIPv4:'{}')", + request.method, + request.host, + request.url, + request.xForwardedFor, + ipv4ToString(meta.sourceIPData)); + } else if (discoverySessionFlagsIsIPv6(meta.flags)) { + LOG_DEBUG( + "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', sourceIPv6:'{}')", + request.method, + request.host, + request.url, + request.xForwardedFor, + ipv6ToString(meta.sourceIPData)); + } else { + LOG_DEBUG( + "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}')", + request.method, + request.host, + request.url, + request.xForwardedFor); + } +} + void Discovery::handleCloseEvent(DiscoveryEvent& event) { if (auto it{savedSessions.find(event.dataKey)}; it != savedSessions.end()) { savedSessions.erase(it); @@ -158,60 +183,15 @@ int Discovery::bpfDiscoveryResumeCollecting() { static uint32_t zero{0}; DiscoveryGlobalState discoveryGlobalState{}; return bpf_map__update_elem( - discoverySkel->maps.globalStateMap, &zero, sizeof(zero), &discoveryGlobalState, sizeof(discoveryGlobalState), BPF_EXIST); + discoverySkel()->maps.globalStateMap, &zero, sizeof(zero), &discoveryGlobalState, sizeof(discoveryGlobalState), BPF_EXIST); } int Discovery::bpfDiscoveryResetConfig() { return bpfDiscoveryResumeCollecting(); } -bool Discovery::isLoaded() noexcept { - return discoverySkel != nullptr && loaded; -} - -void Discovery::load() { - LIBBPF_OPTS(bpf_object_open_opts, openOpts); - discoverySkelOpenOpts = openOpts; - - if (int res{ensure_core_btf(&openOpts)}) { - throw std::runtime_error("Failed to fetch necessary BTF for CO-RE: " + std::string(strerror(-res))); - } - - discoverySkel = discovery_bpf__open_opts(&openOpts); - if (discoverySkel == nullptr) { - throw std::runtime_error("Failed to open BPF object."); - } - - if (int res{discovery_bpf__load(discoverySkel)}) { - throw std::runtime_error("Failed to load BPF object: " + std::to_string(res)); - } - - if (int res{discovery_bpf__attach(discoverySkel)}) { - throw std::runtime_error("Failed to attach BPF object: " + std::to_string(res)); - } - - if (int res{bpfDiscoveryResumeCollecting()}) { - throw std::runtime_error("Failed to set config of BPF program: " + std::to_string(res)); - } - - loaded = true; -} - -void Discovery::unload() noexcept { - stopRun(); - loaded = false; - if (discoverySkel != nullptr) { - discovery_bpf__destroy(discoverySkel); - } - cleanup_core_btf(&discoverySkelOpenOpts); -} - -void Discovery::stopRun() { - running = false; -} - void Discovery::handleSuccessfulParse(const Session& session, const DiscoverySessionMeta& meta) { - printSession(session, meta); + handleNewRequest(session, meta); } void Discovery::saveSession(const DiscoverySavedSessionKey& sessionKey, const Session& session) { @@ -219,7 +199,7 @@ void Discovery::saveSession(const DiscoverySavedSessionKey& sessionKey, const Se } int Discovery::bpfDiscoveryDeleteSession(const DiscoveryTrackedSessionKey& trackedSessionKey) { - return bpf_map__delete_elem(discoverySkel->maps.trackedSessionsMap, &trackedSessionKey, sizeof(trackedSessionKey), BPF_ANY); + return bpf_map__delete_elem(discoverySkel()->maps.trackedSessionsMap, &trackedSessionKey, sizeof(trackedSessionKey), BPF_ANY); } } // namespace ebpfdiscovery diff --git a/libebpfdiscovery/src/DiscoveryBpf.cpp b/libebpfdiscovery/src/DiscoveryBpf.cpp new file mode 100644 index 00000000..285c9ca6 --- /dev/null +++ b/libebpfdiscovery/src/DiscoveryBpf.cpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "ebpfdiscovery/DiscoveryBpf.h" + +namespace ebpfdiscovery { + +DiscoveryBpf::DiscoveryBpf(discovery_bpf* skel) : skel(skel) { +} + +} // namespace ebpfdiscovery diff --git a/libebpfdiscovery/src/DiscoveryBpfLoader.cpp b/libebpfdiscovery/src/DiscoveryBpfLoader.cpp new file mode 100644 index 00000000..f85ca8da --- /dev/null +++ b/libebpfdiscovery/src/DiscoveryBpfLoader.cpp @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "ebpfdiscovery/DiscoveryBpfLoader.h" + +#include "logging/Global.h" + +extern "C" { +#include "bpfload/btf_helpers.h" +} + +namespace ebpfdiscovery { + +DiscoveryBpfLoader::DiscoveryBpfLoader() { +} + +DiscoveryBpfLoader::~DiscoveryBpfLoader() { + unload(); +} + +void DiscoveryBpfLoader::load() { + LOG_DEBUG("Loading BPF program."); + LIBBPF_OPTS(bpf_object_open_opts, newOpenOpts); + openOpts = newOpenOpts; + + if (int res{ensure_core_btf(&openOpts)}) { + throw std::runtime_error("Failed to fetch necessary BTF for CO-RE: " + std::string(strerror(-res))); + } + + skel = discovery_bpf__open_opts(&openOpts); + if (skel == nullptr) { + throw std::runtime_error("Failed to open BPF object."); + } + + if (int res{discovery_bpf__load(skel)}) { + throw std::runtime_error("Failed to load BPF object: " + std::to_string(res)); + } + + if (int res{discovery_bpf__attach(skel)}) { + throw std::runtime_error("Failed to attach BPF object: " + std::to_string(res)); + } + + loaded = true; +} + +void DiscoveryBpfLoader::unload() noexcept { + loaded = false; + if (skel != nullptr) { + discovery_bpf__destroy(skel); + } + cleanup_core_btf(&openOpts); +} + +bool DiscoveryBpfLoader::isLoaded() noexcept { + return skel != nullptr && loaded; +} + +DiscoveryBpf DiscoveryBpfLoader::get() { + return DiscoveryBpf(skel); +} + +} // namespace ebpfdiscovery diff --git a/libebpfdiscoveryskel/src/Config.h b/libebpfdiscoveryskel/src/Config.h new file mode 100644 index 00000000..ff08a924 --- /dev/null +++ b/libebpfdiscoveryskel/src/Config.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#pragma once + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, struct DiscoveryConfig); + __uint(max_entries, 1); +} discoveryConfigMap SEC(".maps"); + +__attribute__((always_inline)) inline static struct DiscoveryConfig* getDiscoveryConfig() { + __u32 zero = 0; + return (struct DiscoveryConfig*)bpf_map_lookup_elem(&discoveryConfigMap, &zero); +} diff --git a/liblogging/CMakeLists.txt b/liblogging/CMakeLists.txt new file mode 100644 index 00000000..37a016d3 --- /dev/null +++ b/liblogging/CMakeLists.txt @@ -0,0 +1,6 @@ +list(APPEND SOURCES src/Logger.cpp src/Global.cpp) +set(TARGET logging) + +add_library(${TARGET} SHARED ${SOURCES}) +target_include_directories(${TARGET} PRIVATE src PUBLIC headers) +target_link_libraries(${TARGET} spdlog::spdlog) diff --git a/liblogging/headers/logging/Global.h b/liblogging/headers/logging/Global.h new file mode 100644 index 00000000..0bea988c --- /dev/null +++ b/liblogging/headers/logging/Global.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "logging/Logger.h" + +namespace logging { + +class Global { +public: + static Logger& getInstance(); + +private: + Global() = default; +}; + +#define LOG_TRACE(...) logging::Global::getInstance().trace(__VA_ARGS__) +#define LOG_DEBUG(...) logging::Global::getInstance().debug(__VA_ARGS__) +#define LOG_INFO(...) logging::Global::getInstance().info(__VA_ARGS__) +#define LOG_WARN(...) logging::Global::getInstance().warn(__VA_ARGS__) +#define LOG_ERROR(...) logging::Global::getInstance().error(__VA_ARGS__) +#define LOG_CRITICAL(...) logging::Global::getInstance().critical(__VA_ARGS__) + +} // namespace logging diff --git a/liblogging/headers/logging/Logger.h b/liblogging/headers/logging/Logger.h new file mode 100644 index 00000000..c1fd8140 --- /dev/null +++ b/liblogging/headers/logging/Logger.h @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace logging { + +enum LogLevel { + Trace = static_cast(spdlog::level::trace), + Debug = static_cast(spdlog::level::debug), + Info = static_cast(spdlog::level::info), + Warn = static_cast(spdlog::level::warn), + Err = static_cast(spdlog::level::err), + Critical = static_cast(spdlog::level::critical), + Off = static_cast(spdlog::level::off) +}; + +std::istream& operator>>(std::istream& in, LogLevel& level); + +class Logger { +private: + static constexpr std::size_t max_size = 102400; + static constexpr std::size_t max_files = 3; + +public: + Logger(); + ~Logger() = default; + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + + void setLevel(enum LogLevel level); + void setup(std::string name = {}, bool logToStdout = true, const std::string_view& logDir = {}); + + template + void log(enum LogLevel level, const char* fmt, const Args&... args) { + spdLogger.log(static_cast(level), fmt, args...); + } + + void vlogf(enum LogLevel level, const char* format, va_list args); + + template + void trace(const char* fmt, const Args&... args) { + spdLogger.trace(fmt, args...); + } + + template + void debug(const char* fmt, const Args&... args) { + spdLogger.debug(fmt, args...); + } + + template + void info(const char* fmt, const Args&... args) { + spdLogger.info(fmt, args...); + } + + template + void warn(const char* fmt, const Args&... args) { + spdLogger.warn(fmt, args...); + } + + template + void error(const char* fmt, const Args&... args) { + spdLogger.error(fmt, args...); + } + + template + void critical(const char* fmt, const Args&... args) { + spdLogger.critical(fmt, args...); + } + + template + void trace(const char* str) { + spdLogger.trace(str); + } + + template + void debug(const char* str) { + spdLogger.debug(str); + } + + template + void info(const char* str) { + spdLogger.info(str); + } + + template + void warn(const char* str) { + spdLogger.warn(str); + } + + template + void error(const char* str) { + spdLogger.error(str); + } + + template + void critical(const char* str) { + spdLogger.critical(str); + } + +private: + void loggerSetDefaults(); + void setLogger(spdlog::logger logger); + void logLine(enum LogLevel level, const char* str, size_t len); + + spdlog::logger spdLogger; + int id = rand(); +}; + +} // namespace logging diff --git a/liblogging/src/Global.cpp b/liblogging/src/Global.cpp new file mode 100644 index 00000000..f37a9c3c --- /dev/null +++ b/liblogging/src/Global.cpp @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "logging/Global.h" + +namespace logging { + +Logger& Global::getInstance() { + static Logger instance; + return instance; +} + +} // namespace logging diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp new file mode 100644 index 00000000..d1ecd67e --- /dev/null +++ b/liblogging/src/Logger.cpp @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "logging/Logger.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace logging { + +std::istream& operator>>(std::istream& in, LogLevel& level) { + std::string token; + in >> token; + std::transform(token.begin(), token.end(), token.begin(), [](unsigned char c) { return std::tolower(c); }); + if (token == "trace") { + level = LogLevel::Trace; + } else if (token == "debug") { + level = LogLevel::Debug; + } else if (token == "info") { + level = LogLevel::Info; + } else if (token == "warning" || token == "warn") { + level = LogLevel::Warn; + } else if (token == "error" || token == "err") { + level = LogLevel::Err; + } else if (token == "critical" || token == "crit") { + level = LogLevel::Critical; + } else if (token == "off") { + level = LogLevel::Off; + } else { + in.setstate(std::ios::failbit); + } + return in; +} + +Logger::Logger() : spdLogger("default") { + loggerSetDefaults(); +} + +void Logger::setLevel(enum LogLevel level) { + spdLogger.set_level(static_cast(level)); +} + +void Logger::setup(std::string name, bool logToStdout, const std::string_view& logDir) { + namespace fs = std::filesystem; + + if (name.empty()) { + name = "default"; + } + + std::vector sinks; + if (logToStdout) { + sinks.push_back(std::make_shared()); + } + + if (!logDir.empty()) { + if (!fs::is_directory(logDir)) { + throw std::runtime_error("Log directory doesn't exist or is not a directory"); + } + // TODO: Check for permissions + std::string logFile = fmt::format("{}/{}.log", logDir, name); + sinks.push_back(std::make_shared(logFile, max_size, max_files)); + } + + setLogger(spdlog::logger(name, sinks.begin(), sinks.end())); +} + +void Logger::vlogf(enum LogLevel level, const char* format, va_list args) { + if (!spdLogger.should_log(static_cast(level))) { + return; + } + const int staticBufSize = 512; + static char staticBuf[staticBufSize]{}; + + int resultSize = vsnprintf(staticBuf, staticBufSize, format, args); + if (resultSize <= staticBufSize) { + logLine(level, staticBuf, resultSize); + return; + } + + static va_list args2; + va_copy(args2, args); + + char buf[resultSize]{}; + vsnprintf(buf, resultSize, format, args2); + + logLine(level, buf, resultSize); +} + +void Logger::setLogger(spdlog::logger logger) { + spdLogger = std::move(logger); + loggerSetDefaults(); +} + +void Logger::loggerSetDefaults() { + spdLogger.set_level(spdlog::level::info); + spdLogger.set_pattern("%Y-%m-%d %H:%M:%S.%e [%t] %^%l%$ [%n] %v"); +} + +void Logger::logLine(enum LogLevel level, const char* buf, size_t len) { + if (buf == nullptr || len == 0) { + return; + } + + std::string str; + if (buf[len - 1] == '\n') { + str.assign(buf, len - 1); + } else { + str.assign(buf, len); + } + log(level, "{}", str); +} + +} // namespace logging From d7300aa1f88ae8058b0524a45185a15c99c603f5 Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 09:09:32 +0200 Subject: [PATCH 02/16] build: Remove commented out file entry --- libebpfdiscovery/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/libebpfdiscovery/CMakeLists.txt b/libebpfdiscovery/CMakeLists.txt index 65c2f31f..55d94c33 100644 --- a/libebpfdiscovery/CMakeLists.txt +++ b/libebpfdiscovery/CMakeLists.txt @@ -5,7 +5,6 @@ list( src/Discovery.cpp src/DiscoveryBpf.cpp src/DiscoveryBpfLoader.cpp - # src/DiscoveryBpfLogHandler.cpp src/Session.cpp src/StringFunctions.cpp ) From 0a9c0bba3cda48d4e34c66e42fb70b4e6a8d140a Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 09:11:24 +0200 Subject: [PATCH 03/16] refactor: explicitly initialize running bool in Discovery --- libebpfdiscovery/headers/ebpfdiscovery/Discovery.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h b/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h index 736a44bf..c4e44180 100644 --- a/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h +++ b/libebpfdiscovery/headers/ebpfdiscovery/Discovery.h @@ -65,7 +65,7 @@ class Discovery { DiscoveryBpf discoveryBpf; SavedSessionsCacheType savedSessions; - std::atomic running; + std::atomic running{false}; bool stopReceived{false}; std::condition_variable stopReceivedCV; std::mutex stopReceivedMutex; From 234c023e508c6678704aedc7b530a1c5989b5997 Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 09:17:52 +0200 Subject: [PATCH 04/16] fix(liblogging): copy va_args before reuse --- liblogging/src/Logger.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index d1ecd67e..4463b9bd 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -78,17 +78,17 @@ void Logger::vlogf(enum LogLevel level, const char* format, va_list args) { const int staticBufSize = 512; static char staticBuf[staticBufSize]{}; + static va_list argsCopy; + va_copy(argsCopy, args); + int resultSize = vsnprintf(staticBuf, staticBufSize, format, args); if (resultSize <= staticBufSize) { logLine(level, staticBuf, resultSize); return; } - static va_list args2; - va_copy(args2, args); - char buf[resultSize]{}; - vsnprintf(buf, resultSize, format, args2); + vsnprintf(buf, resultSize, format, argsCopy); logLine(level, buf, resultSize); } @@ -116,5 +116,4 @@ void Logger::logLine(enum LogLevel level, const char* buf, size_t len) { } log(level, "{}", str); } - } // namespace logging From d0db18b0fe89a6c522b0971826e1015719776023 Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 09:20:38 +0200 Subject: [PATCH 05/16] feat(liblogger): set loglevel to off by default --- liblogging/src/Logger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index 4463b9bd..0fdc7f1f 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -99,7 +99,7 @@ void Logger::setLogger(spdlog::logger logger) { } void Logger::loggerSetDefaults() { - spdLogger.set_level(spdlog::level::info); + spdLogger.set_level(spdlog::level::off); spdLogger.set_pattern("%Y-%m-%d %H:%M:%S.%e [%t] %^%l%$ [%n] %v"); } From 9bc36f18543de57dd3683fe5d042ea461a839b1c Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 09:28:42 +0200 Subject: [PATCH 06/16] refactor(liblogging): remove unused id member from Logger --- liblogging/headers/logging/Logger.h | 1 - 1 file changed, 1 deletion(-) diff --git a/liblogging/headers/logging/Logger.h b/liblogging/headers/logging/Logger.h index c1fd8140..f6caafeb 100644 --- a/liblogging/headers/logging/Logger.h +++ b/liblogging/headers/logging/Logger.h @@ -112,7 +112,6 @@ class Logger { void logLine(enum LogLevel level, const char* str, size_t len); spdlog::logger spdLogger; - int id = rand(); }; } // namespace logging From 366572614456957ef099aa8a85bfda3f8e3678ef Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 10:31:33 +0200 Subject: [PATCH 07/16] refactor(liblogging): use path type instead of string --- ebpfdiscoverysrv/src/main.cpp | 10 +++++----- liblogging/headers/logging/Logger.h | 3 ++- liblogging/src/Logger.cpp | 10 ++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index 8153dbae..059f5eeb 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -5,9 +5,9 @@ #include "logging/Global.h" #include -#include #include +#include #include #include #include @@ -36,7 +36,7 @@ static boost::program_options::options_description getProgramOptions() { desc.add_options() ("log-level", po::value()->default_value(logging::LogLevel::Err, "error"), "Set log level {trace,debug,info,warning,error,critical,off}") ("help,h", "Display available options") - ("log-dir", po::value()->default_value(""), "Log files directory") + ("log-dir", po::value()->default_value(""), "Log files directory") ("log-no-stdout", po::value()->default_value(false), "Disable logging to stdout") ("version", "Display program version") ; @@ -55,10 +55,10 @@ static std::string getProgramVersion() { * Logging setup */ -static void setupLogging(logging::LogLevel logLevel, bool enableStdout, std::string_view logDir) { +static void setupLogging(logging::LogLevel logLevel, bool enableStdout, const std::filesystem::path& logDir) { logging::Global::getInstance().setup("eBPF-Discovery", enableStdout, logDir); logging::Global::getInstance().setLevel(logLevel); - LOG_TRACE("Logging has been set up. (logDir: {})", logDir); + LOG_TRACE("Logging has been set up. (logDir: {})", logDir.string()); } /* @@ -139,7 +139,7 @@ int main(int argc, char** argv) { logging::LogLevel logLevel{vm["log-level"].as()}; bool isStdoutLogDisabled{vm["log-no-stdout"].as()}; - std::string logDir{vm["log-dir"].as()}; + std::filesystem::path logDir{vm["log-dir"].as()}; try { setupLogging(logLevel, !isStdoutLogDisabled, logDir); diff --git a/liblogging/headers/logging/Logger.h b/liblogging/headers/logging/Logger.h index f6caafeb..5b9ccb30 100644 --- a/liblogging/headers/logging/Logger.h +++ b/liblogging/headers/logging/Logger.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ class Logger { Logger& operator=(const Logger&) = delete; void setLevel(enum LogLevel level); - void setup(std::string name = {}, bool logToStdout = true, const std::string_view& logDir = {}); + void setup(std::string name, bool logToStdout = true, std::filesystem::path logDir = {}); template void log(enum LogLevel level, const char* fmt, const Args&... args) { diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index 0fdc7f1f..a57af93c 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -47,13 +47,9 @@ void Logger::setLevel(enum LogLevel level) { spdLogger.set_level(static_cast(level)); } -void Logger::setup(std::string name, bool logToStdout, const std::string_view& logDir) { +void Logger::setup(std::string name, bool logToStdout, std::filesystem::path logDir) { namespace fs = std::filesystem; - if (name.empty()) { - name = "default"; - } - std::vector sinks; if (logToStdout) { sinks.push_back(std::make_shared()); @@ -64,7 +60,8 @@ void Logger::setup(std::string name, bool logToStdout, const std::string_view& l throw std::runtime_error("Log directory doesn't exist or is not a directory"); } // TODO: Check for permissions - std::string logFile = fmt::format("{}/{}.log", logDir, name); + + fs::path logFile = logDir / (name + ".log"); sinks.push_back(std::make_shared(logFile, max_size, max_files)); } @@ -75,6 +72,7 @@ void Logger::vlogf(enum LogLevel level, const char* format, va_list args) { if (!spdLogger.should_log(static_cast(level))) { return; } + const int staticBufSize = 512; static char staticBuf[staticBufSize]{}; From cc093d7390934fb130fcaf78ddeebe465f8b2f6f Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 10:35:17 +0200 Subject: [PATCH 08/16] refactor(liblogging): remove default logger name --- liblogging/src/Logger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index a57af93c..ece80fd0 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -39,7 +39,7 @@ std::istream& operator>>(std::istream& in, LogLevel& level) { return in; } -Logger::Logger() : spdLogger("default") { +Logger::Logger() : spdLogger("") { loggerSetDefaults(); } From 40b7596d2404d75d9f8c0d2e78d600cc769cf129 Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Thu, 5 Oct 2023 10:38:03 +0200 Subject: [PATCH 09/16] fix(libloggign): add len check in vlogf --- liblogging/src/Logger.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index ece80fd0..b330d17a 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -61,7 +61,7 @@ void Logger::setup(std::string name, bool logToStdout, std::filesystem::path log } // TODO: Check for permissions - fs::path logFile = logDir / (name + ".log"); + fs::path logFile{logDir / (name + ".log")}; sinks.push_back(std::make_shared(logFile, max_size, max_files)); } @@ -73,13 +73,13 @@ void Logger::vlogf(enum LogLevel level, const char* format, va_list args) { return; } - const int staticBufSize = 512; + const int staticBufSize{512}; static char staticBuf[staticBufSize]{}; static va_list argsCopy; va_copy(argsCopy, args); - int resultSize = vsnprintf(staticBuf, staticBufSize, format, args); + int resultSize{vsnprintf(staticBuf, staticBufSize, format, args)}; if (resultSize <= staticBufSize) { logLine(level, staticBuf, resultSize); return; @@ -107,7 +107,7 @@ void Logger::logLine(enum LogLevel level, const char* buf, size_t len) { } std::string str; - if (buf[len - 1] == '\n') { + if (len > 1 && buf[len - 1] == '\n') { str.assign(buf, len - 1); } else { str.assign(buf, len); From 81494d4cee9fa37b7991f99d9b0c02cd805cfe9d Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Mon, 9 Oct 2023 09:18:15 +0200 Subject: [PATCH 10/16] feat(ebpfdiscoverysrv): update use of project version macro --- ebpfdiscoverysrv/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index 059f5eeb..9664da8b 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -47,7 +47,7 @@ static boost::program_options::options_description getProgramOptions() { static std::string getProgramVersion() { std::ostringstream ss; - ss << EBPFDISCOVERY_VERSION_MAJOR << "." << EBPFDISCOVERY_VERSION_MINOR << "." << EBPFDISCOVERY_VERSION_PATCH; + ss << PROJECT_VERSION; return ss.str(); } From 07a9fafcf251c5a4bc920b6e0d798dfaae82c6ec Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Tue, 10 Oct 2023 11:17:14 +0200 Subject: [PATCH 11/16] refator(discoverysrv): make boost::program_options namespace alias global --- ebpfdiscoverysrv/src/main.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index 9664da8b..cd8381ee 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -14,6 +14,9 @@ #include #include +namespace po = boost::program_options; +using logging::LogLevel; + enum class ProgramStatus { Running, UnixShutdownSignalReceived, @@ -26,10 +29,7 @@ std::mutex programStatusMutex; * CLI options */ -using logging::LogLevel; - -static boost::program_options::options_description getProgramOptions() { - namespace po = boost::program_options; +static po::options_description getProgramOptions() { po::options_description desc{"Options"}; // clang-format off @@ -40,8 +40,8 @@ static boost::program_options::options_description getProgramOptions() { ("log-no-stdout", po::value()->default_value(false), "Disable logging to stdout") ("version", "Display program version") ; - // clang-format on + return desc; } @@ -115,7 +115,6 @@ static void setupLibbpf() { } int main(int argc, char** argv) { - namespace po = boost::program_options; po::options_description desc{getProgramOptions()}; po::variables_map vm; From c15ac42d852bf35410eaae36820b3d0f2e4ad1fc Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Tue, 10 Oct 2023 11:33:30 +0200 Subject: [PATCH 12/16] refactor(liblogging): Make Logger the singleton class --- ebpfdiscoverysrv/src/main.cpp | 13 ++++++------ libebpfdiscovery/src/Discovery.cpp | 2 +- libebpfdiscovery/src/DiscoveryBpfLoader.cpp | 2 +- liblogging/CMakeLists.txt | 2 +- liblogging/headers/logging/Global.h | 23 --------------------- liblogging/headers/logging/Logger.h | 9 ++++++++ liblogging/src/Global.cpp | 11 ---------- liblogging/src/Logger.cpp | 5 +++++ 8 files changed, 24 insertions(+), 43 deletions(-) delete mode 100644 liblogging/headers/logging/Global.h delete mode 100644 liblogging/src/Global.cpp diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index cd8381ee..a741e82f 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -2,7 +2,7 @@ #include "ebpfdiscovery/Discovery.h" #include "ebpfdiscovery/DiscoveryBpf.h" #include "ebpfdiscovery/DiscoveryBpfLoader.h" -#include "logging/Global.h" +#include "logging/Logger.h" #include @@ -15,6 +15,7 @@ #include namespace po = boost::program_options; +using logging::Logger; using logging::LogLevel; enum class ProgramStatus { @@ -56,8 +57,8 @@ static std::string getProgramVersion() { */ static void setupLogging(logging::LogLevel logLevel, bool enableStdout, const std::filesystem::path& logDir) { - logging::Global::getInstance().setup("eBPF-Discovery", enableStdout, logDir); - logging::Global::getInstance().setLevel(logLevel); + Logger::getInstance().setup("eBPF-Discovery", enableStdout, logDir); + Logger::getInstance().setLevel(logLevel); LOG_TRACE("Logging has been set up. (logDir: {})", logDir.string()); } @@ -98,13 +99,13 @@ static void runUnixSignalHandlerLoop() { static int libbpfPrintFn(enum libbpf_print_level level, const char* format, va_list args) { switch (level) { case LIBBPF_WARN: - logging::Global::getInstance().vlogf(logging::LogLevel::Warn, format, args); + Logger::getInstance().vlogf(logging::LogLevel::Warn, format, args); return 0; case LIBBPF_INFO: - logging::Global::getInstance().vlogf(logging::LogLevel::Info, format, args); + Logger::getInstance().vlogf(logging::LogLevel::Info, format, args); return 0; case LIBBPF_DEBUG: - logging::Global::getInstance().vlogf(logging::LogLevel::Debug, format, args); + Logger::getInstance().vlogf(logging::LogLevel::Debug, format, args); return 0; } return 0; diff --git a/libebpfdiscovery/src/Discovery.cpp b/libebpfdiscovery/src/Discovery.cpp index 23227e38..7e04ffbd 100644 --- a/libebpfdiscovery/src/Discovery.cpp +++ b/libebpfdiscovery/src/Discovery.cpp @@ -3,7 +3,7 @@ #include "StringFunctions.h" #include "ebpfdiscovery/Session.h" -#include "logging/Global.h" +#include "logging/Logger.h" #include #include diff --git a/libebpfdiscovery/src/DiscoveryBpfLoader.cpp b/libebpfdiscovery/src/DiscoveryBpfLoader.cpp index f85ca8da..8fba5a12 100644 --- a/libebpfdiscovery/src/DiscoveryBpfLoader.cpp +++ b/libebpfdiscovery/src/DiscoveryBpfLoader.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "ebpfdiscovery/DiscoveryBpfLoader.h" -#include "logging/Global.h" +#include "logging/Logger.h" extern "C" { #include "bpfload/btf_helpers.h" diff --git a/liblogging/CMakeLists.txt b/liblogging/CMakeLists.txt index 37a016d3..9b114895 100644 --- a/liblogging/CMakeLists.txt +++ b/liblogging/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND SOURCES src/Logger.cpp src/Global.cpp) +list(APPEND SOURCES src/Logger.cpp) set(TARGET logging) add_library(${TARGET} SHARED ${SOURCES}) diff --git a/liblogging/headers/logging/Global.h b/liblogging/headers/logging/Global.h deleted file mode 100644 index 0bea988c..00000000 --- a/liblogging/headers/logging/Global.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#include "logging/Logger.h" - -namespace logging { - -class Global { -public: - static Logger& getInstance(); - -private: - Global() = default; -}; - -#define LOG_TRACE(...) logging::Global::getInstance().trace(__VA_ARGS__) -#define LOG_DEBUG(...) logging::Global::getInstance().debug(__VA_ARGS__) -#define LOG_INFO(...) logging::Global::getInstance().info(__VA_ARGS__) -#define LOG_WARN(...) logging::Global::getInstance().warn(__VA_ARGS__) -#define LOG_ERROR(...) logging::Global::getInstance().error(__VA_ARGS__) -#define LOG_CRITICAL(...) logging::Global::getInstance().critical(__VA_ARGS__) - -} // namespace logging diff --git a/liblogging/headers/logging/Logger.h b/liblogging/headers/logging/Logger.h index 5b9ccb30..774a0677 100644 --- a/liblogging/headers/logging/Logger.h +++ b/liblogging/headers/logging/Logger.h @@ -37,6 +37,8 @@ class Logger { Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; + static Logger& getInstance(); + void setLevel(enum LogLevel level); void setup(std::string name, bool logToStdout = true, std::filesystem::path logDir = {}); @@ -116,3 +118,10 @@ class Logger { }; } // namespace logging + +#define LOG_TRACE(...) logging::Logger::getInstance().trace(__VA_ARGS__) +#define LOG_DEBUG(...) logging::Logger::getInstance().debug(__VA_ARGS__) +#define LOG_INFO(...) logging::Logger::getInstance().info(__VA_ARGS__) +#define LOG_WARN(...) logging::Logger::getInstance().warn(__VA_ARGS__) +#define LOG_ERROR(...) logging::Logger::getInstance().error(__VA_ARGS__) +#define LOG_CRITICAL(...) logging::Logger::getInstance().critical(__VA_ARGS__) diff --git a/liblogging/src/Global.cpp b/liblogging/src/Global.cpp deleted file mode 100644 index f37a9c3c..00000000 --- a/liblogging/src/Global.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -#include "logging/Global.h" - -namespace logging { - -Logger& Global::getInstance() { - static Logger instance; - return instance; -} - -} // namespace logging diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index b330d17a..b88e34be 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -43,6 +43,11 @@ Logger::Logger() : spdLogger("") { loggerSetDefaults(); } +Logger& Logger::getInstance() { + static Logger instance; + return instance; +} + void Logger::setLevel(enum LogLevel level) { spdLogger.set_level(static_cast(level)); } From 9ff8fed25bd955c3a7c17ea505fe5dffe84cf68a Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Tue, 10 Oct 2023 11:35:53 +0200 Subject: [PATCH 13/16] refactor(libebpfdiscoverysrv): Use project version macro directly --- ebpfdiscoverysrv/src/main.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index a741e82f..cdd21f96 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -46,12 +46,6 @@ static po::options_description getProgramOptions() { return desc; } -static std::string getProgramVersion() { - std::ostringstream ss; - ss << PROJECT_VERSION; - return ss.str(); -} - /* * Logging setup */ @@ -133,7 +127,7 @@ int main(int argc, char** argv) { } if (vm.count("version")) { - std::cout << "eBPF-Discovery " << getProgramVersion() << '\n'; + std::cout << "eBPF-Discovery " << PROJECT_VERSION << '\n'; return EXIT_SUCCESS; } From 1fceaf0c1cad6ccd2efb28667563d3db911fddda Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Tue, 10 Oct 2023 11:38:23 +0200 Subject: [PATCH 14/16] refactor: use const for return values --- ebpfdiscoverysrv/src/main.cpp | 2 +- libebpfdiscovery/src/DiscoveryBpfLoader.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ebpfdiscoverysrv/src/main.cpp b/ebpfdiscoverysrv/src/main.cpp index cdd21f96..56cc0a0b 100644 --- a/ebpfdiscoverysrv/src/main.cpp +++ b/ebpfdiscoverysrv/src/main.cpp @@ -70,7 +70,7 @@ static void runUnixSignalHandlerLoop() { while (true) { sigset_t sigset{getSigset()}; LOG_TRACE("Waiting for unix signals."); - int signo{sigwaitinfo(&sigset, nullptr)}; + const auto signo{sigwaitinfo(&sigset, nullptr)}; if (signo == -1) { LOG_CRITICAL("Failed to wait for unix signals: {}", std::strerror(errno)); std::abort(); diff --git a/libebpfdiscovery/src/DiscoveryBpfLoader.cpp b/libebpfdiscovery/src/DiscoveryBpfLoader.cpp index 8fba5a12..6950d79e 100644 --- a/libebpfdiscovery/src/DiscoveryBpfLoader.cpp +++ b/libebpfdiscovery/src/DiscoveryBpfLoader.cpp @@ -21,7 +21,7 @@ void DiscoveryBpfLoader::load() { LIBBPF_OPTS(bpf_object_open_opts, newOpenOpts); openOpts = newOpenOpts; - if (int res{ensure_core_btf(&openOpts)}) { + if (const auto res{ensure_core_btf(&openOpts)}) { throw std::runtime_error("Failed to fetch necessary BTF for CO-RE: " + std::string(strerror(-res))); } @@ -30,11 +30,11 @@ void DiscoveryBpfLoader::load() { throw std::runtime_error("Failed to open BPF object."); } - if (int res{discovery_bpf__load(skel)}) { + if (const auto res{discovery_bpf__load(skel)}) { throw std::runtime_error("Failed to load BPF object: " + std::to_string(res)); } - if (int res{discovery_bpf__attach(skel)}) { + if (const auto res{discovery_bpf__attach(skel)}) { throw std::runtime_error("Failed to attach BPF object: " + std::to_string(res)); } From 90f4fd77652d4c71022e5fab5202a9acfb7edccd Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Tue, 10 Oct 2023 11:54:36 +0200 Subject: [PATCH 15/16] feat(liblogging): Add permission check of logdir in setup --- liblogging/src/Logger.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/liblogging/src/Logger.cpp b/liblogging/src/Logger.cpp index b88e34be..64b7bc68 100644 --- a/liblogging/src/Logger.cpp +++ b/liblogging/src/Logger.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace logging { @@ -61,10 +62,17 @@ void Logger::setup(std::string name, bool logToStdout, std::filesystem::path log } if (!logDir.empty()) { + if (!fs::exists(logDir)) { + throw std::runtime_error("Log directory doesn't exist"); + } + if (!fs::is_directory(logDir)) { - throw std::runtime_error("Log directory doesn't exist or is not a directory"); + throw std::runtime_error("Log directory is not a directory"); + } + + if (access(logDir.c_str(), R_OK | W_OK) != 0) { + throw std::runtime_error("Couldn't access log directory for reading and writing"); } - // TODO: Check for permissions fs::path logFile{logDir / (name + ".log")}; sinks.push_back(std::make_shared(logFile, max_size, max_files)); @@ -117,6 +125,8 @@ void Logger::logLine(enum LogLevel level, const char* buf, size_t len) { } else { str.assign(buf, len); } + log(level, "{}", str); } + } // namespace logging From c70470013df0973c6ae3c22b6c1a30a18b3f2205 Mon Sep 17 00:00:00 2001 From: "Parzych, Hubert" Date: Tue, 10 Oct 2023 12:03:54 +0200 Subject: [PATCH 16/16] feat(libebpfdiscovery): Add logging PID during handling of a request --- libebpfdiscovery/src/Discovery.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libebpfdiscovery/src/Discovery.cpp b/libebpfdiscovery/src/Discovery.cpp index 7e04ffbd..1040f3bb 100644 --- a/libebpfdiscovery/src/Discovery.cpp +++ b/libebpfdiscovery/src/Discovery.cpp @@ -149,27 +149,30 @@ void Discovery::handleNewRequest(const Session& session, const DiscoverySessionM const auto& request{session.parser.result}; if (discoverySessionFlagsIsIPv4(meta.flags)) { LOG_DEBUG( - "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', sourceIPv4:'{}')", + "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', sourceIPv4:'{}', pid:{})", request.method, request.host, request.url, request.xForwardedFor, - ipv4ToString(meta.sourceIPData)); + ipv4ToString(meta.sourceIPData), + meta.pid); } else if (discoverySessionFlagsIsIPv6(meta.flags)) { LOG_DEBUG( - "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', sourceIPv6:'{}')", + "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', sourceIPv6:'{}', pid:{})", request.method, request.host, request.url, request.xForwardedFor, - ipv6ToString(meta.sourceIPData)); + ipv6ToString(meta.sourceIPData), + meta.pid); } else { LOG_DEBUG( - "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}')", + "Handling new request. (method:'{}', host:'{}', url:'{}', X-Forwarded-For:'{}', pid:{})", request.method, request.host, request.url, - request.xForwardedFor); + request.xForwardedFor, + meta.pid); } }