core/ebpf/EBPFAdapter.cpp (389 lines of code) (raw):
// Copyright 2025 iLogtail Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.
#include "ebpf/EBPFAdapter.h"
#include <memory>
#include <string>
#include "app_config/AppConfig.h"
#include "common/LogtailCommonFlags.h"
#include "common/MachineInfoUtil.h"
#include "common/RuntimeUtil.h"
#include "ebpf/driver/eBPFDriver.h"
#include "logger/Logger.h"
namespace logtail {
namespace ebpf {
#define LOAD_EBPF_FUNC_ADDR(funcName) \
({ \
void* funcPtr = tmp_lib->LoadMethod(#funcName, loadErr); \
if (funcPtr == NULL) { \
LOG_ERROR(sLogger, \
("[source_manager] load ebpf method", "failed")("method", #funcName)("error", loadErr)); \
} \
funcPtr; \
})
#define LOAD_UPROBE_OFFSET_FAIL (-100)
#define LOAD_UPROBE_OFFSET(funcAddr) \
({ \
Dl_info dlinfo; \
int dlAddrErr = dladdr((const void*)(funcAddr), &dlinfo); \
long res = 0; \
if (dlAddrErr == 0) { \
LOG_ERROR(sLogger, \
("[source_manager] load ebpf dl address", "failed")("error", dlAddrErr)("method", #funcAddr)); \
res = LOAD_UPROBE_OFFSET_FAIL; \
} else { \
res = (long)dlinfo.dli_saddr - (long)dlinfo.dli_fbase; \
LOG_DEBUG(sLogger, ("func", #funcAddr)("offset", res)); \
} \
res; \
})
#define LOAD_EBPF_FUNC_AND_UPROBE_OFFSET(funcName) \
({ \
void* funcPtr = tmp_lib->LoadMethod(#funcName, loadErr); \
long offset = 0; \
if (funcPtr == NULL) { \
LOG_ERROR(sLogger, \
("[source_manager] load ebpf method", "failed")("method", #funcName)("error", loadErr)); \
offset = LOAD_UPROBE_OFFSET_FAIL; \
} else { \
Dl_info dlinfo; \
int dlAddrErr = dladdr((const void*)(funcPtr), &dlinfo); \
if (dlAddrErr == 0) { \
LOG_ERROR( \
sLogger, \
("[source_manager] load ebpf dl address", "failed")("error", dlAddrErr)("method", #funcName)); \
offset = LOAD_UPROBE_OFFSET_FAIL; \
} else { \
offset = (long)dlinfo.dli_saddr - (long)dlinfo.dli_fbase; \
LOG_DEBUG(sLogger, ("func", #funcName)("offset", offset)); \
} \
} \
offset; \
})
EBPFAdapter::EBPFAdapter() = default;
EBPFAdapter::~EBPFAdapter() {
if (!dynamicLibSuccess()) {
return;
}
for (size_t i = 0; i < mRunning.size(); i++) {
auto& x = mRunning[i];
if (!x) {
continue;
}
// stop plugin
StopPlugin(static_cast<PluginType>(i));
}
#ifdef APSARA_UNIT_TEST_MAIN
return;
#endif
}
void EBPFAdapter::Init() {
mBinaryPath = GetProcessExecutionDir();
mFullLibName = "lib" + mDriverLibName + ".so";
for (auto& x : mRunning) {
x = false;
}
mLogPrinter = [](int16_t level, const char* format, va_list args) -> int {
eBPFLogType printLevel = (eBPFLogType)level;
switch (printLevel) {
case eBPFLogType::NAMI_LOG_TYPE_WARN:
if (!SHOULD_LOG_WARNING(sLogger)) {
return 0;
}
break;
case eBPFLogType::NAMI_LOG_TYPE_DEBUG:
if (!SHOULD_LOG_DEBUG(sLogger)) {
return 0;
}
break;
case eBPFLogType::NAMI_LOG_TYPE_INFO:
[[fallthrough]];
default:
if (!SHOULD_LOG_INFO(sLogger)) {
return 0;
}
break;
}
char buffer[4096] = {0};
vsnprintf(buffer, sizeof(buffer), format, args);
buffer[sizeof(buffer) - 1] = '\0';
switch (printLevel) {
case eBPFLogType::NAMI_LOG_TYPE_WARN:
LOG_WARNING(sLogger, ("module", "eBPFDriver")("msg", buffer));
break;
case eBPFLogType::NAMI_LOG_TYPE_INFO:
LOG_INFO(sLogger, ("module", "eBPFDriver")("msg", buffer));
break;
case eBPFLogType::NAMI_LOG_TYPE_DEBUG:
LOG_DEBUG(sLogger, ("module", "eBPFDriver")("msg", buffer));
break;
default:
LOG_INFO(sLogger, ("module", "eBPFDriver")("level", int(level))("msg", buffer));
break;
}
return 0;
};
}
bool EBPFAdapter::loadDynamicLib(const std::string& libName) {
if (dynamicLibSuccess()) {
// already load
return true;
}
std::shared_ptr<DynamicLibLoader> tmp_lib = std::make_shared<DynamicLibLoader>();
LOG_INFO(sLogger, ("[EBPFAdapter] begin load ebpf dylib, path:", mBinaryPath));
std::string loadErr;
if (!tmp_lib->LoadDynLib(libName, loadErr, mBinaryPath)) {
LOG_ERROR(sLogger, ("failed to load ebpf dynamic library, path", mBinaryPath)("error", loadErr));
return false;
}
// load method
mFuncs[static_cast<int>(ebpf_func::EBPF_SET_LOGGER)] = LOAD_EBPF_FUNC_ADDR(set_logger);
mFuncs[static_cast<int>(ebpf_func::EBPF_START_PLUGIN)] = LOAD_EBPF_FUNC_ADDR(start_plugin);
mFuncs[static_cast<int>(ebpf_func::EBPF_UPDATE_PLUGIN)] = LOAD_EBPF_FUNC_ADDR(update_plugin);
mFuncs[static_cast<int>(ebpf_func::EBPF_STOP_PLUGIN)] = LOAD_EBPF_FUNC_ADDR(stop_plugin);
mFuncs[static_cast<int>(ebpf_func::EBPF_SUSPEND_PLUGIN)] = LOAD_EBPF_FUNC_ADDR(suspend_plugin);
mFuncs[static_cast<int>(ebpf_func::EBPF_RESUME_PLUGIN)] = LOAD_EBPF_FUNC_ADDR(resume_plugin);
mFuncs[static_cast<int>(ebpf_func::EBPF_POLL_PLUGIN_PBS)] = LOAD_EBPF_FUNC_ADDR(poll_plugin_pbs);
mFuncs[static_cast<int>(ebpf_func::EBPF_SET_NETWORKOBSERVER_CONFIG)]
= LOAD_EBPF_FUNC_ADDR(set_networkobserver_config);
mFuncs[static_cast<int>(ebpf_func::EBPF_SET_NETWORKOBSERVER_CID_FILTER)]
= LOAD_EBPF_FUNC_ADDR(set_networkobserver_cid_filter);
mFuncs[static_cast<int>(ebpf_func::EBPF_MAP_UPDATE_ELEM)] = LOAD_EBPF_FUNC_ADDR(update_bpf_map_elem);
// check function load success
if (std::any_of(mFuncs.begin(), mFuncs.end(), [](auto* x) { return x == nullptr; })) {
return false;
}
// load offset ...
if (!loadCoolBPF()) {
LOG_ERROR(sLogger, ("failed to load coolbpf", ""));
return false;
}
// set global logger ...
auto eBPFSetLogger = (set_logger_func)mFuncs[static_cast<int>(ebpf_func::EBPF_SET_LOGGER)];
if (!eBPFSetLogger) {
LOG_WARNING(
sLogger,
("cannot set logger for ebpf driver, because set_logger func was load incorrectly ... ", "please check"));
} else {
eBPFSetLogger(mLogPrinter);
}
// update meta
mLib = std::move(tmp_lib);
return true;
}
bool EBPFAdapter::loadCoolBPF() {
if (dynamicLibSuccess()) {
// already load
return true;
}
std::shared_ptr<DynamicLibLoader> tmp_lib = std::make_shared<DynamicLibLoader>();
LOG_INFO(sLogger, ("[EBPFAdapter] begin load libcoolbpf, path:", mBinaryPath));
std::string loadErr;
if (!tmp_lib->LoadDynLib("coolbpf", loadErr, mBinaryPath, ".1.0.0")) {
LOG_ERROR(sLogger, ("failed to load libcoolbpf, path", mBinaryPath)("error", loadErr));
return false;
}
// load address
mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_CLEAN_UP_DOG)]
= LOAD_EBPF_FUNC_AND_UPROBE_OFFSET(ebpf_cleanup_dog);
mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_UPDATE_CONN_ROLE)]
= LOAD_EBPF_FUNC_AND_UPROBE_OFFSET(ebpf_update_conn_role);
mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_DISABLE_PROCESS)]
= LOAD_EBPF_FUNC_AND_UPROBE_OFFSET(ebpf_disable_process);
mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_UPDATE_CONN_ADDR)]
= LOAD_EBPF_FUNC_AND_UPROBE_OFFSET(ebpf_update_conn_addr);
if (!std::all_of(mOffsets.begin(), mOffsets.end(), [](auto x) { return x > 0; })) {
LOG_ERROR(sLogger, ("failed to load libcoolbpf funcs addr, path", mBinaryPath));
return false;
}
mCoolbpfLib = std::move(tmp_lib);
return true;
}
bool EBPFAdapter::dynamicLibSuccess() {
// #ifdef APSARA_UNIT_TEST_MAIN
// return true;
// #endif
if (!mLib) {
return false;
}
if (!std::all_of(mFuncs.begin(), mFuncs.end(), [](auto* x) { return x != nullptr; })) {
return false;
}
return true;
}
bool EBPFAdapter::CheckPluginRunning(PluginType pluginType) {
if (!loadDynamicLib(mDriverLibName)) {
LOG_ERROR(sLogger, ("dynamic lib not load, plugin type:", int(pluginType)));
return false;
}
return mRunning[int(pluginType)];
}
bool EBPFAdapter::SetNetworkObserverConfig(int32_t key, int32_t value) {
if (!dynamicLibSuccess()) {
return false;
}
void* f = mFuncs[static_cast<int>(ebpf_func::EBPF_SET_NETWORKOBSERVER_CONFIG)];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, set networkobserver config func ptr is null", ""));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto func = (set_networkobserver_config_func)f;
func(key, value);
return true;
#endif
}
bool EBPFAdapter::SetNetworkObserverCidFilter(const std::string& cid, bool update) {
if (!dynamicLibSuccess()) {
return false;
}
void* f = mFuncs[static_cast<int>(ebpf_func::EBPF_SET_NETWORKOBSERVER_CID_FILTER)];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, set networkobserver config func ptr is null", ""));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto func = (set_networkobserver_cid_filter_func)f;
func(cid.c_str(), cid.size(), update);
return true;
#endif
}
int32_t EBPFAdapter::PollPerfBuffers(PluginType pluginType, int32_t maxEvents, int32_t* flag, int timeoutMs) {
if (!dynamicLibSuccess()) {
return -1;
}
void* f = mFuncs[static_cast<int>(ebpf_func::EBPF_POLL_PLUGIN_PBS)];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, poll perf buffer func ptr is null", int(pluginType)));
return -1;
}
#ifdef APSARA_UNIT_TEST_MAIN
return 0;
#else
auto pollFunc = (poll_plugin_pbs_func)f;
return pollFunc(pluginType, maxEvents, flag, timeoutMs);
#endif
}
bool EBPFAdapter::StartPlugin(PluginType pluginType, std::unique_ptr<PluginConfig> conf) {
if (CheckPluginRunning(pluginType)) {
// plugin update ...
return UpdatePlugin(pluginType, std::move(conf));
}
// plugin not started ...
LOG_INFO(sLogger, ("begin to start plugin, type", int(pluginType)));
if (conf->mPluginType == PluginType::NETWORK_OBSERVE) {
auto* nconf = std::get_if<NetworkObserveConfig>(&conf->mConfig);
if (nconf) {
nconf->mSo = mBinaryPath + "libcoolbpf.so.1.0.0";
nconf->mLogHandler = mLogPrinter;
nconf->mUpcaOffset
= mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_UPDATE_CONN_ADDR)];
nconf->mUprobeOffset
= mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_CLEAN_UP_DOG)];
nconf->mUpcrOffset
= mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_UPDATE_CONN_ROLE)];
nconf->mUppsOffset
= mOffsets[static_cast<int>(network_observer_uprobe_funcs::EBPF_NETWORK_OBSERVER_DISABLE_PROCESS)];
}
}
void* f = mFuncs[(int)ebpf_func::EBPF_START_PLUGIN];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, init func ptr is null", int(pluginType)));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto startF = (start_plugin_func)f;
int res = startF(conf.get());
if (!res)
mRunning[int(pluginType)] = true;
return !res;
#endif
}
bool EBPFAdapter::ResumePlugin(PluginType pluginType, std::unique_ptr<PluginConfig> conf) {
if (!CheckPluginRunning(pluginType)) {
LOG_ERROR(sLogger, ("plugin not started, type", int(pluginType)));
return false;
}
LOG_INFO(sLogger, ("begin to resume plugin, type", int(pluginType)));
void* f = mFuncs[(int)ebpf_func::EBPF_RESUME_PLUGIN];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, update func ptr is null", int(pluginType)));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto resumeF = (resume_plugin_func)f;
int res = resumeF(conf.get());
return !res;
#endif
}
bool EBPFAdapter::UpdatePlugin(PluginType pluginType, std::unique_ptr<PluginConfig> conf) {
if (!CheckPluginRunning(pluginType)) {
LOG_ERROR(sLogger, ("plugin not started, type", int(pluginType)));
return false;
}
LOG_INFO(sLogger, ("begin to update plugin, type", int(pluginType)));
void* f = mFuncs[(int)ebpf_func::EBPF_UPDATE_PLUGIN];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, update func ptr is null", int(pluginType)));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto updateF = (update_plugin_func)f;
int res = updateF(conf.get());
return !res;
#endif
}
bool EBPFAdapter::SuspendPlugin(PluginType pluginType) {
if (!CheckPluginRunning(pluginType)) {
LOG_WARNING(sLogger, ("plugin not started, cannot suspend. type", int(pluginType)));
return false;
}
void* f = mFuncs[(int)ebpf_func::EBPF_SUSPEND_PLUGIN];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, suspend func ptr is null", int(pluginType)));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto suspendF = (suspend_plugin_func)f;
int res = suspendF(pluginType);
return !res;
#endif
}
bool EBPFAdapter::StopPlugin(PluginType pluginType) {
if (!CheckPluginRunning(pluginType)) {
LOG_WARNING(sLogger, ("plugin not started, do nothing. type", int(pluginType)));
return true;
}
auto config = std::make_unique<PluginConfig>();
config->mPluginType = pluginType;
void* f = mFuncs[(int)ebpf_func::EBPF_STOP_PLUGIN];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, stop func ptr is null", int(pluginType)));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto stopF = (stop_plugin_func)f;
int res = stopF(pluginType);
if (!res)
mRunning[int(pluginType)] = false;
return !res;
#endif
}
bool EBPFAdapter::BPFMapUpdateElem(
PluginType pluginType, const std::string& map_name, void* key, void* value, uint64_t flag) {
if (!CheckPluginRunning(pluginType)) {
LOG_WARNING(sLogger, ("plugin not started, do nothing. type", int(pluginType)));
return true;
}
void* f = mFuncs[(int)ebpf_func::EBPF_MAP_UPDATE_ELEM];
if (!f) {
LOG_ERROR(sLogger, ("failed to load dynamic lib, update bpf map elem func ptr is null", int(pluginType)));
return false;
}
#ifdef APSARA_UNIT_TEST_MAIN
return true;
#else
auto ff = (update_bpf_map_elem_func)f;
int res = ff(pluginType, map_name.c_str(), key, value, flag);
return res == 0;
#endif
}
} // namespace ebpf
} // namespace logtail