blob: d2be9ab586d27522fcd4b197690e3f9f6fb7aebc [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SECAGENTD_BPF_SKELETON_WRAPPERS_H_
#define SECAGENTD_BPF_SKELETON_WRAPPERS_H_
#include <memory>
#include <string>
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/functional/callback.h"
#include "secagentd/bpf/process.h"
#include "secagentd/bpf_skeletons/skeleton_process_bpf.h"
namespace secagentd {
// Directory with min_core_btf payloads. Must match the ebuild.
constexpr char kMinCoreBtfDir[] = "/usr/share/btf/secagentd/";
// The following callback definitions must have void return type since they will
// bind to an object method.
using BpfEventCb = base::RepeatingCallback<void(const bpf::cros_event&)>;
using BpfEventAvailableCb = base::RepeatingCallback<void()>;
// The callbacks a BPF plugins are required to provide.
struct BpfCallbacks {
// The callback responsible for handling a ring buffer security event.
BpfEventCb ring_buffer_event_callback;
// The callback that handles when any ring buffer has data ready for
// consumption (reading).
BpfEventAvailableCb ring_buffer_read_ready_callback;
};
class BpfSkeletonInterface {
public:
explicit BpfSkeletonInterface(const BpfSkeletonInterface&) = delete;
BpfSkeletonInterface& operator=(const BpfSkeletonInterface&) = delete;
virtual ~BpfSkeletonInterface() = default;
// Consume one or more events from a BPF ring buffer, ignoring whether a ring
// buffer has notified that data is available for read.
virtual int ConsumeEvent() = 0;
protected:
friend class BpfSkeletonFactory;
BpfSkeletonInterface() = default;
virtual absl::Status LoadAndAttach() = 0;
// Register callbacks to handle:
// 1 - When a security event from a ring buffer has been consumed and is
// available for further processing.
// 2 - When a ring buffer has data available for reading.
virtual void RegisterCallbacks(BpfCallbacks cbs) = 0;
};
class ProcessBpfSkeleton : public BpfSkeletonInterface {
public:
~ProcessBpfSkeleton() override;
int ConsumeEvent() override;
protected:
friend class BpfSkeletonFactory;
absl::Status LoadAndAttach() override;
void RegisterCallbacks(BpfCallbacks cbs) override;
private:
BpfCallbacks callbacks_;
process_bpf* skel_{nullptr};
struct ring_buffer* rb_{nullptr};
std::unique_ptr<base::FileDescriptorWatcher::Controller> rb_watch_readable_;
};
class BpfSkeletonFactoryInterface
: public ::base::RefCounted<BpfSkeletonFactoryInterface> {
public:
enum class BpfSkeletonType { kProcess };
struct SkeletonInjections {
std::unique_ptr<BpfSkeletonInterface> process;
};
// Creates a BPF Handler class that loads and attaches a BPF application.
// The passed in callback will be invoked when an event is available from the
// BPF application.
virtual std::unique_ptr<BpfSkeletonInterface> Create(BpfSkeletonType type,
BpfCallbacks cbs) = 0;
virtual ~BpfSkeletonFactoryInterface() = default;
};
namespace Types {
using BpfSkeleton = BpfSkeletonFactoryInterface::BpfSkeletonType;
} // namespace Types
// Support absl format for BpfSkeletonType.
absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
AbslFormatConvert(const BpfSkeletonFactoryInterface::BpfSkeletonType& type,
const absl::FormatConversionSpec& spec,
absl::FormatSink* sink);
std::ostream& operator<<(
std::ostream& out,
const BpfSkeletonFactoryInterface::BpfSkeletonType& type);
class BpfSkeletonFactory : public BpfSkeletonFactoryInterface {
public:
BpfSkeletonFactory() = default;
explicit BpfSkeletonFactory(SkeletonInjections di) : di_(std::move(di)) {}
std::unique_ptr<BpfSkeletonInterface> Create(BpfSkeletonType type,
BpfCallbacks cbs) override;
private:
SkeletonInjections di_;
absl::flat_hash_set<BpfSkeletonType> created_skeletons_;
};
} // namespace secagentd
#endif // SECAGENTD_BPF_SKELETON_WRAPPERS_H_