core/ebpf/driver/BPFWrapper.h (336 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. #pragma once extern "C" { #include <bpf/libbpf.h> #include <coolbpf/coolbpf.h> }; #include <unistd.h> #include <atomic> #include <map> #include <memory> #include <string> #include <unordered_map> #include <vector> #include "BPFMapTraits.h" #include "Log.h" namespace logtail { namespace ebpf { struct PerfBufferOps { public: PerfBufferOps(const std::string& name, ssize_t size, perf_buffer_sample_fn scb, perf_buffer_lost_fn lcb) : name(name), size(size), sampleCb(scb), lostCb(lcb) {} std::string name; ssize_t size; perf_buffer_sample_fn sampleCb; perf_buffer_lost_fn lostCb; }; struct AttachProgOps { public: AttachProgOps(const std::string& name, bool attach) : name(name), attach(attach) {} std::string name; bool attach; }; class BPFWrapperBase { public: virtual ~BPFWrapperBase() = default; }; inline constexpr int kErrInitSkel = 1; inline constexpr int kErrLibbpf = 2; inline constexpr int kErrNotFound = -1; template <typename T> class BPFWrapper : public BPFWrapperBase { public: static std::shared_ptr<BPFWrapper<T>> Create() { return std::make_shared<BPFWrapper<T>>(); } ~BPFWrapper() { Destroy(); } /** * Init will open and load bpf object, and fill caches for maps and progs */ int Init() { if (mInited) { return 0; } mInited = true; mSkel = T::open_and_load(); mFlag = true; if (!mSkel) { return kErrInitSkel; } bpf_map* map = nullptr; bpf_object__for_each_map(map, mSkel->obj) { const char* name = bpf_map__name(map); mBpfMaps[name] = map; } struct bpf_program* prog = nullptr; bpf_object__for_each_program(prog, mSkel->obj) { const char* name = bpf_program__name(prog); mBpfProgs[name] = prog; } return 0; } /** * attach bpf programs */ int DynamicAttachBPFObject(const std::vector<AttachProgOps>& ops) { int err = 0; for (const auto& op : ops) { if (!op.attach) { continue; } auto it = mBpfProgs.find(op.name); if (it == mBpfProgs.end() || it->second == nullptr) { continue; } bpf_program* prog = it->second; bpf_link* link = bpf_program__attach(prog); err = libbpf_get_error(link); if (err) { continue; } mLinks.insert({op.name, link}); } return 0; } /** * detach bpf programs */ int DynamicDetachBPFObject(const std::vector<AttachProgOps>& ops) { for (const auto& op : ops) { auto it = mLinks.find(op.name); if (it == mLinks.end()) { continue; } auto* link = it->second; // do detach auto err = bpf_link__destroy(link); if (err) { continue; } // remove from map mLinks.erase(it); } return 0; } /** * set tail calls */ int SetTailCall(const std::string& mapName, const std::vector<std::string>& functions) { int mapFd = SearchMapFd(mapName); if (mapFd < 0) { return kErrNotFound; } for (int i = 0; i < (int)functions.size(); i++) { const auto& func = functions[i]; int funcFd = SearchProgFd(func); if (funcFd <= 0) { continue; } int ret = bpf_map_update_elem(mapFd, &i, &funcFd, 0); if (ret) { // abnormal } } return 0; } template <typename MapInMapType> int DeleteInnerMap(const std::string& outterMapName, void* outterKey) { int mapFd = SearchMapFd(outterMapName); if (mapFd < 0) { return kErrNotFound; } // delete bpf map bpf_map_delete_elem(mapFd, outterKey); int* key = static_cast<int*>(outterKey); // get inner map fd from outter map fd and outter key // close fd for inner map int innerFd = -1; if (mApInMapFds[mapFd].count(*key)) { innerFd = mApInMapFds[mapFd][*key]; } if (innerFd > 0) { close(innerFd); } return 0; } template <typename MapInMapType> int DeleteInnerMapElem(const std::string& outterMapName, void* outterKey, void* innerKey) { int mapFd = SearchMapFd(outterMapName); if (mapFd < 0) { return kErrNotFound; } int innerMapFd = -1; uint32_t innerMapId = 0; int ret = bpf_map_lookup_elem(mapFd, outterKey, &innerMapId); if (ret) { return 0; } innerMapFd = bpf_map_get_fd_by_id(innerMapId); if (innerMapFd < 0) { return kErrNotFound; } ret = bpf_map_delete_elem(innerMapFd, innerKey); close(innerMapFd); return ret; } template <typename MapInMapType> int UpdateInnerMapElem( const std::string& outterMapName, void* outterKey, void* innerKey, void* innerValue, uint64_t flag) { int mapFd = SearchMapFd(outterMapName); if (mapFd < 0) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][UpdateInnerMapElem] find outter hash map failed for: %s \n", outterMapName.c_str()); return kErrNotFound; } int innerMapFd = -1; uint32_t innerMapId = 0; int ret = bpf_map_lookup_elem(mapFd, outterKey, &innerMapId); if (ret) { struct bpf_map_create_opts* popt = nullptr; struct bpf_map_create_opts opt {}; if (BPFMapTraits<MapInMapType>::map_flag != -1) { ::memset(&opt, 0, sizeof(struct bpf_map_create_opts)); // opt.map_extra = ; opt.sz = sizeof(opt); opt.map_flags = BPF_F_NO_PREALLOC; popt = &opt; } // TODO @qianlu.kk recycle this fd when distroy // we don't need free inner bpf map manually, since kernel will hold ref count for every bpf map // when we destroy outter map, the inner maps that holds will be destroy by kernel ... int fd = bpf_map_create(BPFMapTraits<MapInMapType>::inner_map_type, NULL, BPFMapTraits<MapInMapType>::inner_key_size, BPFMapTraits<MapInMapType>::inner_val_size, BPFMapTraits<MapInMapType>::inner_max_entries, popt); if (fd < 0) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][UpdateInnerMapElem] failed to create inner map for outter map: %s \n", outterMapName.c_str()); return kErrLibbpf; } int* key = static_cast<int*>(outterKey); mApInMapFds[mapFd][*key] = fd; ret = bpf_map_update_elem(mapFd, outterKey, &fd, BPF_ANY); close(fd); } ret = bpf_map_lookup_elem(mapFd, outterKey, &innerMapId); if (ret) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][UpdateInnerMapElem] failed to lookup inner map for outter map: %s \n", outterMapName.c_str()); return ret; } innerMapFd = bpf_map_get_fd_by_id(innerMapId); if (innerMapFd < 0) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][UpdateInnerMapElem] failed to find inner map fd by id for outter map: %s \n", outterMapName.c_str()); return kErrNotFound; } ret = bpf_map_update_elem(innerMapFd, innerKey, innerValue, flag); close(innerMapFd); return ret; } /** * update elements from bpf map */ int UpdateBPFHashMap(const std::string& mapName, void* key, void* value, uint64_t flag) { int mapFd = SearchMapFd(mapName); ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_DEBUG, "[BPFWrapper][UpdateBPFHashMap] find map name: %s map fd: %d \n", mapName.c_str(), mapFd); if (mapFd < 0) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_DEBUG, "[BPFWrapper][UpdateBPFHashMap] find hash map failed for: %s \n", mapName.c_str()); return kErrNotFound; } return bpf_map_update_elem(mapFd, key, value, flag); } /** * lookup element from bpf map */ int LookupBPFHashMap(const std::string& mapName, void* key, void* value) { int mapFd = SearchMapFd(mapName); ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_DEBUG, "[BPFWrapper][LookupBPFHashMap] find map name: %s map fd: %d \n", mapName.c_str(), mapFd); if (mapFd < 0) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_DEBUG, "[BPFWrapper][LookupBPFHashMap] find hash map failed for: %s \n", mapName.c_str()); return kErrNotFound; } return bpf_map_lookup_elem(mapFd, key, value); } /** * remove element from bpf map */ int RemoveBPFHashMap(const std::string& mapName, void* key) { int mapFd = SearchMapFd(mapName); ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_DEBUG, "[BPFWrapper][RemoveBPFHashMap] find map name: %s map fd: %d \n", mapName.c_str(), mapFd); if (mapFd < 0) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_DEBUG, "[BPFWrapper][RemoveBPFHashMap] find hash map failed for: %s \n", mapName.c_str()); return kErrNotFound; } bpf_map_delete_elem(mapFd, key); return 0; } void DeletePerfBuffer(void* pb) { perf_buffer__free((struct perf_buffer*)pb); } int PollPerfBuffer(void* pb, int /*maxEvents*/, int timeoutMs) { return perf_buffer__poll((struct perf_buffer*)pb, timeoutMs); } void* CreatePerfBuffer( const std::string& name, int pageCnt, void* ctx, perf_buffer_sample_fn dataCb, perf_buffer_lost_fn lossCb) { int mapFd = SearchMapFd(name); if (mapFd < 0) { return nullptr; } struct perf_buffer_opts pbOpts = {}; pbOpts.sample_cb = dataCb; pbOpts.ctx = ctx; pbOpts.lost_cb = lossCb; struct perf_buffer* pb = NULL; pb = perf_buffer__new(mapFd, pageCnt == 0 ? 128 : pageCnt, &pbOpts); auto err = libbpf_get_error(pb); if (err) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][CreatePerfBuffer] error new perf buffer: %s \n", strerror(-err)); return nullptr; } if (!pb) { err = -errno; ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][CreatePerfBuffer] failed to open perf buffer: %ld \n", err); return nullptr; } return pb; } int DetachAllPerfBuffers() { return 0; } /** * Destroy skel and release resources. */ void Destroy() { if (!mInited) { return; } ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_INFO, "[BPFWrapper] begin destroy \n"); // LOG(INFO) << "begin to destroy bpf wrapper"; // clear all links first for (auto& it : mLinks) { auto* link = it.second; auto err = bpf_link__destroy(link); if (err) { ebpf_log(logtail::ebpf::eBPFLogType::NAMI_LOG_TYPE_WARN, "[BPFWrapper][Destroy] failed to destroy link, err: %d \n", err); } } mLinks.clear(); mBpfMaps.clear(); mBpfProgs.clear(); // destroy skel T::destroy(mSkel); // stop perf threads ... mFlag = false; DetachAllPerfBuffers(); mInited = false; } int SearchProgFd(const std::string& name) { auto it = mBpfProgs.find(name); if (it == mBpfProgs.end()) { return kErrNotFound; } return bpf_program__fd(it->second); } int SearchMapFd(const std::string& name) { auto it = mBpfMaps.find(name); if (it == mBpfMaps.end()) { return kErrNotFound; } return bpf_map__fd(it->second); } int GetBPFProgFdById(int id) { return bpf_prog_get_fd_by_id(id); } private: // {map_name, map_fd} std::map<std::string, bpf_map*> mBpfMaps; // {map_name, prog_fd} std::map<std::string, bpf_program*> mBpfProgs; std::map<std::string, bpf_link*> mLinks; std::unordered_map<int, std::unordered_map<int, int>> mApInMapFds; T* mSkel = nullptr; volatile bool mInited = false; std::atomic_bool mFlag = false; // links, used for strore bpf programs friend class NetworkSecurityManager; }; } // namespace ebpf } // namespace logtail